Как напечатать собственное имя скрипта в mawk?

13

В bash $0содержится имя скрипта, но в awk, если я сделаю скрипт с именем myscript.awk со следующим содержанием:

#!/usr/bin/awk -f
BEGIN{ print ARGV[0] }

и запустить его, он будет печатать только «awk». Кроме того, ARGV [i] с i> 0 используется только для аргументов скрипта в командной строке. Итак, как заставить его напечатать имя скрипта, в данном случае «myscript.awk»?

cipper
источник
Я изменил название с awk на mawk, потому что все решения требуют gawk и не работают с общим awk, и в частности с mawk, который широко используется (например, по умолчанию в Ubuntu)
cipper
Что заставляет вас думать, что mawkпо умолчанию в Ubuntu? На моей 15.04 ВМ значение по умолчанию awk- gawk. Хотя mawk установлен, он не используется по умолчанию.
Тердон
1
Это сценарий awk, если вы его называете awk -f myscript.awk. Однако это не связано с рассматриваемой проблемой.
Чиппер
1
@EdMorton Это awkскрипт, потому что он начинается с #!/usr/bin/awk -f. Скрипты оболочки начинаются с #!/bin/sh(или чего-то подобного).
Бармар
1
Я разговаривал с различными экспертами по оболочкам и пытался получить однозначный ответ о том, является ли это сценарий оболочки или awk, и, что удивительно, согласно POSIX интерпретация файлов начинается с #! не определено и не имеет конкретного имени типа. В то время как некоторые люди называют его «сценарием интерпретации хэша-взрыва», а не сценарием оболочки или awk, по-видимому, все согласны с тем, что его следует рассматривать как сценарий awk, хотя ядро ​​(а не оболочка) интерпретирует первую строку, поскольку awk по-прежнему должен иметь возможность анализировать эту первую строку (как комментарий), и вы можете выполнить ее, используя awk -f file.
Эд Мортон

Ответы:

5

С GNU awk 4.1.3 в bash на cygwin:

$ cat tst.sh
#!/bin/awk -f
BEGIN { print "Executing:", ENVIRON["_"] }

$ ./tst.sh
Executing: ./tst.sh

Я не знаю, насколько это портативно. Однако, как всегда, я бы не стал выполнять сценарий awk, используя shebang в сценарии оболочки, поскольку он просто лишил вас возможной функциональности. Сохраняйте это простым и просто сделайте это вместо этого:

$ cat tst2.sh
awk -v cmd="$0" '
BEGIN { print "Executing:", cmd }
' "$@"

$ ./tst2.sh
Executing: ./tst2.sh

Этот последний будет работать с любым современным awk в любой оболочке на любой платформе.

Эд Мортон
источник
Обратите внимание, что первый работает только в bash, zsh или ksh. Последнее касается сценария оболочки, а не сценария awk.
Cuonglm
2
Спасибо! ENVIRON["_"]работает отлично, и не вызывает никаких внешних программ. Второй вариант awk -v ...зависит от того, как запускается скрипт; Я не хочу этого
Чиппер
1
Вызов вашего сценария tst.shвводит в заблуждение. Это awkсценарий, а не сценарий оболочки. BEGINне является допустимой командой оболочки.
Бармар
1
Правильно, но вопрос переносимости заключается не в том, «является ли ENVIRON [] переносимым», это « ENVIRON["_"]создает ли путь сценария вызывающей оболочки при печати из каждого awk, вызываемого через shebang из каждой оболочки»? Я бы никогда не назвал сценарий awk из шебанга, мне лично наплевать на ответ, а просто подумал, что упомяну его… О, я вижу в комментариях выше, что @cuonglm ответил, что он поддерживается только в некоторых оболочках. ,
Эд Мортон
1
Хороший вопрос, @Ed. Проверяется как сбой в dash (который возвращает предыдущую команду (или саму оболочку), а не текущую). ksh93 интересен префиксами PID в звездочках, например *12345*/tmp/test.awk. ARGV[0]надежно всегда awkв dash, bash, zsh и ksh93.
Адам Кац
5

Я не думаю, что это возможно согласно gawk документации :

Наконец, значение ARGV[0](см. Раздел 7.5 «Встроенные переменные») зависит от вашей операционной системы. Некоторые системы помещают awkтуда, некоторые вводят полный путь к awk (например, /bin/awk), а некоторые - имя вашего скрипта ('advice'). Не полагайтесь на значение ARGV[0]предоставления имени вашего скрипта.

На linuxвас может попытаться использовать вид грязный хака и как указано в комментариях Stéphane Chazelas это возможно , если реализация awkопора NUL байт:

#!/usr/bin/awk -f

BEGIN { getline t < "/proc/self/cmdline"; split(t, a, "\0"); print a[3]; }
taliezin
источник
ваш сценарий как есть, кажется, не работает. Он просто печатает «k», если вызывается с помощью «awk -f script.awk», и печатает «s», если вызывается с помощью «./script.awk»
cipper
@cipper: Здесь это работает с gawkи терпит неудачу (как ваше описание) с mawk. Интересный!
Это работает для меня в Linux, awk- 4.0.2. В freebsd с /proc/curpoc/cmdline, и awkрезультат похож на ваш, но работает с gawk.
Тализин
По умолчанию Ubuntu это не работает. Было бы неплохо найти портативное решение.
cipper
1
@taliezin: ответ от cuonglm не является решением, так как для этого необходимо вручную ввести скрипт со своим именем. Это как звонить, awk -vNAME="myscript.awk" ./myscript.awkа затем напечатать NAME внутри скрипта. Не решение
Чиппер
5

Я не знаю прямого способа получить имя команды из awk. Однако вы можете найти его через вложенную оболочку.

простак

С помощью GNU awk и psкоманды вы можете использовать идентификатор процесса PROCINFO["PID"]для получения имени команды в качестве обходного пути. Например:

cmdname.awk

#!/usr/bin/gawk -f

BEGIN {
  ("ps -p " PROCINFO["pid"] " -o comm=") | getline CMDNAME
  print CMDNAME
}

mawk и nawk

Вы можете использовать тот же подход, но получить awkPID из $PPIDспециальной переменной оболочки (PID родителя):

cmdname.awk

#!/usr/bin/mawk -f

BEGIN { 
  ("ps -p $PPID -o comm=") | getline CMDNAME
  print CMDNAME
}

тестирование

Запустите скрипт так:

./cmdname.awk

Вывод в обоих случаях:

cmdname.awk
Тор
источник
Я получил ошибку: / bin / sh: 1: -o: не найден
cipper
@cipper: Это работает только с GNU awk, я добавил недостающую строку shebang.
Thor
Из руководства gawk : Согласно POSIX, 'выражение | getline 'является неоднозначным, если выражение содержит операторы без скобок, отличные от' $ '- например,' "echo" "date" | getline 'неоднозначен, потому что оператор конкатенации не заключен в скобки. Вы должны написать это как '("эхо" "дата") | getline ', если вы хотите, чтобы ваша программа была переносимой на все реализации awk.
cipper
1
Если это нужно, gawkэто gawkрешение, а не awkрешение. Я думаю, что @cipper должен добавить свое желание "портативное решение" к вопросу.
1
@Thor: ответ от cuonglm не является решением, так как для этого необходимо вручную ввести скрипт со своим именем. Это как звонить, awk -vNAME="myscript.awk" ./myscript.awkа затем напечатать NAME внутри скрипта. Не решение
Чиппер
4

С POSIX awk:

#!/usr/bin/awk -f

BEGIN {
    print ENVIRON["AWKSCRIPT"]
}

Потом:

AWKSCRIPT=test.awk ./test.awk
test.awk
cuonglm
источник
4
Вы вручную вводите в него имя скрипта, это не
самопечатный
@cipper: Ну, это самый простой и портативный способ, который я могу себе представить.
cuonglm
2
Это все равно что вызвать, awk -vNAME="myscript.awk" ./myscript.awkа затем распечатать переменную NAMEвнутри скрипта. Не решение
Чиппер
@cipper: Это единственный способ, если вы упомянули mawk. И также использование ENVIRONне то же самое, что использование -vNAME="myscript.awk", так как , когда mawkбудет расширяться escape-последовательность в NAME.
Cuonglm
4

Использование GNU awk

Проверка руководства пользователя GNU awk - 7.5.2 Встроенные переменные, передающие информацию, на которую я наткнулся:

PROCINFO #

Элементы этого массива предоставляют доступ к информации о запущенной программе awk. Следующие элементы (перечислены в алфавитном порядке) гарантированно будут доступны:

PROCINFO [ "Pid"]

Идентификатор процесса текущего процесса.

Это означает, что вы можете знать PID программы во время выполнения. Затем, это вопрос использования system()для поиска процесса с данным PID:

#!/usr/bin/gawk -f
BEGIN{ pid=PROCINFO["pid"]
       system("ps -ef | awk '$2==" pid " {print $NF}'")
}

Я использую ps -ef, который отображает PID на 2-й столбец. Предполагая, что выполнение g выполнено, awk -f <script>а других параметров нет, мы можем предположить, что последнее поле строки содержит информацию, которую мы хотим.

Если бы у нас были какие-то параметры, мы должны были бы проанализировать строку по-другому - или, лучше, использовать некоторые параметры ps для печати только тех столбцов, которые нам интересны.

Тестовое задание

$ awk -f a.awk 
a.awk
$ cp a.awk hello.awk
$ awk -f hello.awk 
hello.awk

Также обратите внимание, что в другой главе руководства пользователя GNU awk говорится, что ARGV - это не тот путь:

1.1.4 Исполняемые программы awk

Наконец, значение ARGV [0] (см. Встроенные переменные) варьируется в зависимости от вашей операционной системы. Некоторые системы помещают туда «awk», другие вводят полный путь к awk (например, / bin / awk), а некоторые помещают имя вашего скрипта («advice»). (dc) Не полагайтесь на значение ARGV [0] в качестве имени вашего скрипта.

fedorqui
источник
к сожалению, PROCINFO - это только особенность gawk, а не общий awk. Например, он недоступен в mawk (который установлен по умолчанию в ubuntu)
cipper
Я знаю ... Почему вы пометили вопрос с [gawk] тогда?
Федорки
Ты прав. Когда я отправил вопрос, я не знал обо всех этих различиях между mawk и gawk. Тег теперь изменился на mawk.
Чиппер
@cipper good :) Я на самом деле тестировал mawkи не мог заставить его работать, так что я установил gawkв Ubuntu, и это сработало. Таким образом, обходной путь можно использовать gawk: D
Fedorqui
1
@terdon, gawkпо умолчанию не устанавливается в Ubuntu (или, по крайней мере, в некоторых версиях Ubuntu, где mawkэто awkреализация по умолчанию ). IIRC, мне пришлось установить его и на Debian.
Стефан