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

118

Я хотел бы выполнить сценарий gawk с --re-intervalиспользованием shebang. «Наивный» подход

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

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

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

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

Ханс-Петер Штёрр
источник
1
Глупый эксперимент, который я только что проделал, заключался в том, что один скрипт использовал другой скрипт в строке shebang, что действительно правильно разделяло аргументы.
Hasturkun
@Hasturkun, это поднимает еще одну проблему: поведение строк shebang также отличается от системы к системе в зависимости от того, может ли вызываемая программа сама быть скриптом.
dubiousjim
В последних версиях gawk (> = 4.0) --re-intervalон больше не нужен (см. [ Gnu.org/software/gawk/manual/… ).

Ответы:

25

Мне кажется, это работает с (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.

Аарон МакДэйд
источник
Это мой ответ, но мне интересно, достаточно ли он портативен и надежен. Зависит ли это конкретно от bashPOSIX или будет ли он работать с любым POSIX sh? И я не awkчасто использую , поэтому я не уверен, что мой трюк со второй строкой - хороший способ заставить awkигнорировать строку.
Аарон МакДэйд
Как раз то, что мне было интересно, +1, но, вероятно, нецелесообразно (отсюда относительное количество голосов).
Аарон Холл
Не могли бы вы объяснить, какие у этого могут быть проблемы, @AaronHall? Пока переменная arbitrary_long_nameне конфликтует с переменной, используемой в реальной программе awk, я не вижу никаких проблем. Что-то мне не хватает?
Аарон МакДэйд
Используйте #!/bin/sh -вместо, #!/bin/shчтобы защитить сценарий от возможного неправильного поведения опасным образом, если он вызывается с нулевым аргументом, который имеет -первый символ. Это может произойти случайно в языках программирования, таких как C, где легко случайно испортить, забыв передать имя вызванной программы как часть массива аргументов в execveи аналогичные функции, и если люди обычно забывают защитить от этого, это также может в конечном итоге становятся последним шагом в злонамеренной уязвимости, которая позволяет злоумышленнику получить интерактивную оболочку.
mtraceur
161

Строка 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.]

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

Йорг В. Миттаг
источник
1
В env FreeBSD есть -Sпереключатель, который здесь помогает, но его нет в моем Linux env, и я подозреваю, что он недоступен и в gygwin. @hstoerr, другие пользователи с другими ситуациями могут читать ваши вопросы позже, поэтому в целом переносимые ответы предпочтительнее, даже если вам сейчас не требуется переносимость.
dubiousjim
4
Таким образом, мы не можем переносимо использовать аргументы в shebang. Но что, если нам нужны аргументы любыми способами? Я предполагаю, что решение состоит в том, чтобы написать сценарий оболочки оболочки, содержащий #!/bin/shи /usr/bin/env gawk --re-interval -f my-script.awk. Это правильно?
Рори О'Кейн
1
Я не согласен. Вы можете легко использовать один аргумент. Любая система, в которой вы не можете использовать какие-либо аргументы, с треском не может реализовать этот традиционный Unixism, что и есть hash-bang. Если нереализация - это честная игра, то мы можем с уверенностью сказать, что #!сама по себе не переносима. Например, Windows вообще не распознает это соглашение «изначально». Традиционно в Unix для этого требуется использование одного аргумента #!/usr/bin/awk -f.
Kaz
7
@Kaz: Да, но поскольку пути многих двоичных файлов не стандартизированы, вы используете свой единственный аргумент для #!/usr/bin/env rubyили ему подобных.
Jörg W Mittag
3
@Pacerier: измените спецификацию POSIX и подождите 20-30 лет, пока все системы не будут обновлены для соответствия спецификации.
Jörg W Mittag
18

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

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

unode
источник
1
К вашему сведению, FreeBSD имеет -S в течение многих лет (начиная с 6.0). Это долгожданное дополнение к coreutils.
Хуан
12

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

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

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

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

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

#!/usr/bin/foo -if

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

ℝaphink
источник
11

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

Можно обойти это, используя другой awkскрипт внутри 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в вашей системной оболочке.

Moritz
источник
2
это не сработает, если вы передали аргументы сценарию
Стивен Пенни,
4
#!/bin/sh
''':'
exec YourProg -some_options "$0" "$@"
'''

Приведенный выше трюк с оболочкой shebang более портативен, чем /usr/bin/env.

user3123730
источник
'' ':' - это задержка, потому что мое исходное решение было для скрипта python, поэтому '' ':' сообщает интерпретатору python игнорировать часть exec.
user3123730
4
Я думаю, что вас голосуют против, потому что ваше решение для python, но этот вопрос о awk.
Аарон МакДэйд
1
Отличный хак для Python.
Заар Хай
3

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

ВТА
источник
Похоже, другого способа указать опцию нет. Вы правы: gawk -f - << EOF, несколько строк скриптов, EOF работает, но не позволяет мне читать стандартный ввод с помощью gawk.
Hans-Peter Störr
Документ здесь съедает стандартный входной поток для gawk, но вы все равно можете передать что-то через stderr (то есть перенаправить stdout на stderr, прежде чем подключать к этому скрипту). Я никогда не пробовал этого, но если первый процесс ничего не генерирует на stderr, это может сработать. Вы также можете создать именованный канал ( linuxjournal.com/content/using- named- pipes-fifos-bash ), если хотите убедиться, что ничто другое его не использует.
bta 01
3

Почему бы не использовать 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;)

Конни
источник
0

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

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

Ханс-Петер Штёрр
источник
-1

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

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

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

lharper71
источник
3
У меня это не сработало. Человек bash говорит <<< blabla помещает blabla на стандартный ввод. Вы имели в виду << - EOF? В любом случае это также помещает программу на стандартный ввод.
Hans-Peter Störr