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

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

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

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

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

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

10 ответов

Решение

Кажется, это работает для меня с (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,

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

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

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

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

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

#!/usr/bin/env gawk

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

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

Хотя и не совсем переносимый, начиная с 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

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

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

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

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

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

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

#!/usr/bin/foo -if

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

В 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 в вашей системной оболочке.

#!/bin/sh
''':'
exec YourProg -some_options "$0" "$@"
'''

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

Почему бы не использовать bash а также gawk сам, чтобы пропустить мимо Шебанга, прочитать сценарий и передать его в виде файла во второй экземпляр 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 сам;)

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

Просто для удовольствия: есть следующее довольно странное решение, которое перенаправляет 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

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

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

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

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

Другие вопросы по тегам