Как использовать несколько аргументов для 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. и 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 рассматривал бы как).