Як використовувати кілька аргументів для 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, це викликає ще одне питання, що поведінка рядків shebang також відрізняється від системи до системи wrt, чи може сама викликана програма сама бути сценарієм.
сумнівним


З останніми версіями 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.


Це моя відповідь, але мені цікаво, чи достатньо портативний та надійний. Це залежить конкретно від того bash, чи він працюватиме з будь-яким 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
FreeBSD env має -Sкомутатор, який допомагає тут, але він не присутній у моєму Linux env, і я підозрюю, що він також недоступний для gygwin. @hstoerr, інші користувачі з різними ситуаціями можуть читати ваші запитання пізніше, тому загалом переважні портативні відповіді бажані, навіть якщо зараз вам не потрібна портативність.
сумнівний

4
Тому ми не можемо використовувати аргументи в шебангу. Але що робити, якщо нам потрібні аргументи будь-якими необхідними способами? Я здогадуюсь, що рішення - написати сценарій оболонки оболонки, що містить #!/bin/shі /usr/bin/env gawk --re-interval -f my-script.awk. Це правильно?
Rory O'Kane

1
Я не згоден. Можна досить портативно використовувати один аргумент. Будь-яка система, де ви не можете використовувати будь-які аргументи, не вдається змило реалізувати цей традиційний Unixism. Якщо нереалізація - це чесна гра, то можна сміливо сказати, що #!сама по собі не є портативною. Наприклад, Windows взагалі не розпізнає цю конвенцію "споконвічно". Традиційно для Unix потрібен удар з одною аргументацією, щоб мати можливість це робити #!/usr/bin/awk -f.
Каз

7
@Kaz: Так, але оскільки шляхи багатьох двійкових файлів не стандартизовані, ви використовуєте один аргумент для #!/usr/bin/env rubyабо подібних.
Йорг W Міттаг

3
@Pacerier: Змініть специфікацію POSIX і зачекайте 20-30 років, поки всі системи не будуть оновлені, щоб відповідати специфікації.
Йорг W Міттаг

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

Оригінальна відповідь тут .


1
Зрозуміло, у FreeBSD протягом року існували -S (з 6.0). Це вітальне доповнення для мобільності до Coreutils.
Хуан

12

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

Однак ви можете передавати кілька варіантів в шебангу, якщо вони короткі, і вони можуть бути об'єднані (спосіб GNU).

Наприклад, не можна мати

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

але ти можеш мати

#!/usr/bin/foo -if

Очевидно, що це працює лише тоді, коли варіанти мають короткі еквіваленти і не беруть аргументів.


11

У 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}дивним чином.
І це буде виконуватися /usr/bin/gawk --re-interval -f path/to/your/script.awkв оболонці вашої системи.


2
ця робота не буде, якщо ви передаєте аргументи до сценарію
Стівен Пенні

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

Вищенаведений трюк шебанг-трюк є більш портативним, ніж /usr/bin/env.


'' ':' Є затримкою, тому що моє оригінальне рішення було для сценарію python, тому '' ':' вказує інтерпретатору python ігнорувати частину exec.
користувач3123730

4
Я думаю, що вас зволікають через те, що ваше рішення - це python, але це питання awk.
Аарон Мак-Дейд

1
Відмінний хак для пітона.
Заар Хай

3

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


Здається, немає іншого способу вказати варіант. Ви маєте рацію: gawk -f - << EOF, деякі рядки сценаріїв, EOF працює, але це заважає мені читати стандартний вклад з gawk.
Ганс-Петер Стрерр

Документ тут з'їдає стандартний вхідний потік для gawk, але ви, можливо, все-таки зможете передати щось над stderr (тобто перенаправити stdout на stderr перед тим, як перейти до цього сценарію). Я ніколи насправді цього не пробував, але поки перший процес нічого не видає на stderr, він може працювати. Ви також можете створити названу трубку ( linuxjournal.com/content/using-named-pipes-fifos-bash ), якщо ви хочете переконатися, що більше нічого не використовує.
бта

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

Просто для розваги: ​​є таке досить дивне рішення, яке перенаправляє 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

Одне з цього дратує: оболонка робить змінне розширення для сценарію, тому вам доведеться цитувати кожен $ (як це зроблено у другому рядку сценарію) і, мабуть, більше, ніж це.


-1

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

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

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


3
Це не працювало для мене. Чоловік-баш каже, що <<< blabla ставить blabla на stdin. Ви мали на увазі << - EOF? Так чи інакше, це також ставить програму на stdin.
Ганс-Пітер Штерр
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.