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

Я хотел бы выполнить gawk script с помощью --re-interval с помощью shebang. "Наивный" подход

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

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

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

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

Ответы

Ответ 1

Это, похоже, работает для меня с (g) awk.

#!/bin/sh
arbitrary_long_name==0 "exec" "/usr/bin/gawk" "--re-interval" "-f" "$0" "[email protected]"


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

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

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

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

Ответ 2

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

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

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

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

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

#!/usr/bin/env gawk

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

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

Ответ 3

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

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

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

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

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

#!/usr/bin/foo -if

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

Ответ 4

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

Это можно взломать, используя другой awk script внутри shebang:

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

Ответ 5

Хотя и не совсем переносимый, начиная с 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 "[email protected]"; do
    echo "\$$i is '$arg'"
    i=$((i+1))
done

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

Ответ 6

#!/bin/sh
''':'
exec YourProg -some_options "$0" "[email protected]"
'''

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

Ответ 7

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

Ответ 8

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

Ответ 9

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

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

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

Ответ 10

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

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

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