Как использовать несколько аргументов для awk с шебангом (т.е. #!)? - PullRequest
110 голосов
/ 29 ноября 2010

Я хотел бы выполнить gawk скрипт с --re-interval, используя шебанг.«Наивный» подход

#!/usr/bin/gawk --re-interval -f
... awk script goes here

не работает, поскольку gawk вызывается с первым аргументом "--re-interval -f" (не разбитым вокруг пробела), который он не понимает.Есть ли обходной путь для этого?

Конечно, вы можете либо не вызывать gawk напрямую, но обернуть его в скрипт оболочки, который разделяет первый аргумент, либо создать скрипт оболочки, который затем вызывает gawk, и поместить скрипт в другойфайл, но мне было интересно, если есть какой-то способ сделать это в одном файле.

Поведение линий Шебанга отличается от системы к системе - по крайней мере в Cygwin он не разделяетаргументы по пробелам.Я просто забочусь о том, как сделать это в системе, которая ведет себя так;скрипт не предназначен для переносимости.

Ответы [ 10 ]

151 голосов
/ 29 ноября 2010

Строка shebang никогда не указывается как часть POSIX, SUS, LSB или любой другой спецификации. AFAIK, это даже не было должным образом задокументировано.

Существует грубое согласие относительно того, что он делает: выбирайте все между ! и \n и exec it. Предполагается, что все между ! и \n является полным абсолютным путем к интерпретатору. Нет единого мнения о том, что произойдет, если оно содержит пробелы.

  1. Некоторые операционные системы просто воспринимают все как путь. В конце концов, в большинстве операционных систем пробел или тире допустимы в пути.
  2. Некоторые операционные системы разделяются на пробелы и обрабатывают первую часть как путь к интерпретатору, а остальные - как отдельные аргументы.
  3. Некоторые операционные системы разделяются на первый пробел first и обрабатывают переднюю часть как путь к интерпретатору, а остальные - как аргумент single (это то, что вы видите).
  4. Некоторые даже не поддерживают линии Шебанга вообще .

К счастью, 1. и 4., кажется, вымерли, но 3. довольно широко распространен, поэтому вы просто не можете полагаться на возможность передать более одного аргумента.

И поскольку расположение команд также не указано в POSIX или SUS, вы обычно используете этот единственный аргумент, передавая имя исполняемого файла env, чтобы it могло определить местоположение исполняемого файла; e.g.:

#!/usr/bin/env gawk

[Очевидно, что все еще предполагает определенный путь для env, но существует лишь очень немного систем, где он живет в /bin, так что это в целом безопасно. Расположение env намного более стандартизировано, чем расположение gawk или даже хуже, например python или ruby или spidermonkey.]

Это означает, что вы не можете использовать любые аргументы вообще .

22 голосов
/ 26 сентября 2014

Мне кажется, это работает с (g) awk.

#!/bin/sh
arbitrary_long_name==0 "exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "$@"


# The real awk program starts here
{ print $0 }

Обратите внимание, что #! запускает /bin/sh, поэтому этот сценарий сначала интерпретируется как сценарий оболочки.

Сначала я просто попытался "exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "$@", но awk воспринял это как команду и безоговорочно распечатал каждую строку ввода. Вот почему я вставил arbitrary_long_name==0 - он должен терпеть неудачу все время. Вы могли бы заменить это какой-нибудь бессмысленной строкой. В основном я искал ложное условие в awk, которое не оказало бы неблагоприятного влияния на сценарий оболочки.

В сценарии оболочки arbitrary_long_name==0 определяет переменную с именем arbitrary_long_name и устанавливает ее равной =0.

11 голосов
/ 12 апреля 2016

В Cygwin и Linux все после пути shebang разбирается в программе как один аргумент.

Можно обойти это, используя другой скрипт awk внутри шебанга:

#!/usr/bin/gawk {system("/usr/bin/gawk --re-interval -f " FILENAME); exit}

Это выполнит {system("/usr/bin/gawk --re-interval -f " FILENAME); exit} в awk.
И это выполнит /usr/bin/gawk --re-interval -f path/to/your/script.awk в вашей системной оболочке.

11 голосов
/ 10 февраля 2011

Я столкнулся с той же проблемой, но без видимого решения из-за способа обработки пробелов в шебанге (по крайней мере, в Linux).

Однако вы можете передать несколько параметров в шебанге, если они короткие варианты и они могут быть объединены (способ GNU).

Например, вы не можете иметь

#!/usr/bin/foo -i -f

но вы можете иметь

#!/usr/bin/foo -if

Очевидно, что это работает только тогда, когда параметры имеют короткие эквиваленты и не принимают аргументов.

7 голосов
/ 25 октября 2018

Хотя и не совсем переносимый, начиная с coreutils 8.30 и в соответствии с его документацией , вы сможете использовать:

#!/usr/bin/env -S command arg1 arg2 ...

Так дано:

$ cat test.sh
#!/usr/bin/env -S showargs here 'is another' long arg -e "this and that " too

вы получите:

% ./test.sh 
$0 is '/usr/local/bin/showargs'
$1 is 'here'
$2 is 'is another'
$3 is 'long'
$4 is 'arg'
$5 is '-e'
$6 is 'this and that '
$7 is 'too'
$8 is './test.sh'

и, если вам интересно showargs это:

#!/usr/bin/env sh
echo "\$0 is '$0'"

i=1
for arg in "$@"; do
    echo "\$$i is '$arg'"
    i=$((i+1))
done

Оригинальный ответ здесь .

5 голосов
/ 10 января 2014
#!/bin/sh
''':'
exec YourProg -some_options "$0" "$@"
'''

Вышеупомянутый трюк с Шебангом в оболочке более переносим, ​​чем /usr/bin/env.

3 голосов
/ 13 сентября 2017

Почему бы не использовать bash и gawk для пропуска мимо shebang, чтения сценария и передачи его в виде файла во второй экземпляр gawk [--with-whatever-number-of-params-you-need]?

#!/bin/bash
gawk --re-interval -f <(gawk 'NR>3' $0 )
exit
{
  print "Program body goes here"
  print $1
}

(- то же самое, естественно, может быть достигнуто, например, с помощью sed или tail, но я думаю, что есть какая-то красота, зависящая только от bash и gawk сама;)

3 голосов
/ 29 ноября 2010

В руководстве gawk (http://www.gnu.org/manual/gawk/gawk.html), в конце раздела 1.14 обратите внимание, что вы должны использовать только один аргумент при запуске gawk из строки shebang. Это говорит о том, что ОС будет обрабатывать все после пути к gawk как один аргументВозможно, есть другой способ указать опцию --re-interval? Возможно, ваш скрипт может ссылаться на вашу оболочку в строке shebang, запускать gawk в качестве команды и включать текст вашего скрипта в качестве «здесь документа».

0 голосов
/ 15 марта 2013

Просто для удовольствия: есть следующее довольно странное решение, которое перенаправляет stdin и программу через файловые дескрипторы 3 и 4. Вы также можете создать временный файл для скрипта.

#!/bin/bash
exec 3>&0
exec <<-EOF 4>&0
BEGIN {print "HALLO"}
{print \$1}
EOF
gawk --re-interval -f <(cat 0>&4) 0>&3

Одна вещьЭто раздражает: оболочка выполняет расширение сценария в сценарии, поэтому вы должны заключать в кавычки каждый $ (как это делается во второй строке сценария) и, возможно, больше этого.

0 голосов
/ 14 марта 2013

Для переносимого решения используйте awk вместо gawk, вызовите стандартную оболочку BOURNE (/bin/sh) с помощью своего shebang и вызовите awk напрямую, передав программу в командной строке как документ здесь а не через стандартный ввод:

#!/bin/sh
gawk --re-interval <<<EOF
PROGRAM HERE
EOF

Примечание: нет -f аргумент для awk. Это оставляет stdin доступным для awk для чтения ввода. Предполагая, что у вас установлен gawk и на вашем PATH, это достигает всего, что, я думаю, вы пытались сделать с вашим исходным примером (предполагая, что вы хотите, чтобы содержимое файла представляло собой сценарий awk, а не входные данные, что, по-моему, ваш трах подход бы отнесся к этому как).

...