Сценарий AWK, позволяющий использовать аргументы с префиксом тире - PullRequest
1 голос
/ 20 марта 2019

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

К сожалению, у меня возникли проблемы при попытке передать префикс с тире (-arg) аргументы скрипта, так как вместо этого они интерпретируются AWK.

$ ./script.awk -arg
awk: not an option: -arg

Я заметил опцию --, но я не уверен, как правильно использовать ее в шебанге.Мне не удалось найти способ получить имя файла и сослаться на него в шебанге сценария (что-то вроде #!/usr/bin/awk -f $FILE --).

Тогда я подумал, что, возможно, можно использовать опцию -W exec для решенияпроблема, но я продолжаю получать следующую ошибку (даже не пытаясь использовать с ней опцию --), которая, кажется, предполагает, что имя файла даже не добавляется в конец команды shebang.

$ ./script.awk
awk: vacuous option: -W  exec
awk: 1: unexpected character '.'

Есть ли способ сделать автономный (один файл, без сценария-обертки) исполняемый сценарий AWK, который может принимать аргументы с префиксом тире?


Почему я пытаюсь злоупотреблять AWK до такой степени?Главным образом из любопытства, но также чтобы избавиться от сценария оболочки-оболочки, который я сейчас должен использовать только для выполнения сценария AWK:

#!/bin/sh
awk -f script.awk -- "$@"

Решение должно быть POSIX-совместимым (при условии, что путь AWK/usr/bin/awk).Даже если у вас есть решение, не совместимое с POSIX, пожалуйста, поделитесь им.

1 Ответ

2 голосов
/ 20 марта 2019

Понимание проблемы:

Насколько я понимаю, у ОП есть сложный сценарий под названием script.awk:

#!/usr/bin/awk -f
BEGIN{print "ARGC", ARGC; for(i=0;i<ARGC;++i) print "ARG"i,ARGV[i]}

, который ОП хотел бывызывать с использованием различных традиционных однобуквенных опций в стиле POSIX или длинных опций в стиле GNU.Опции POSIX начинаются с одного символа (-), в то время как длинные опции начинаются с двух символов (--).Это, однако, терпит неудачу, поскольку awk интерпретирует эти аргументы для передачи самому awk, а не списку аргументов сценариев.Например:

$ ./script.awk
ARGC 1
ARG0 awk
$ ./script.awk -arg
awk: not an option: -arg

Вопрос: Есть ли способ написания POSIX-совместимого скрипта, который может обрабатывать такие переносимые аргументы?(Предложения приведены в исходном вопросе.)

Наблюдение 1: Хотя это не сразу ясно, следует отметить, что сообщение об ошибке генерируется mawk и не более распространенная версия GNU gawk .В случае сбоя mawk gawk не делает:

$ mawk -f script.awk -arg
mawk: not an option -arg
$ gawk -f script.awk -arg
ARGC 2
ARG0 gawk
ARG1 -arg

Тем не менее, следует отметить, что как для gawk, так и для mawk можно наблюдать различное поведение, когда аргументы конфликтуют с необязательными аргументами awk.Пример:

$ mawk -f script.awk -var   # this fails as gawk expects -v ar=foo
mawk: improper assignment: -v ar
$ gawk -f script.awk -var   # this fails as gawk expects -v ar=foo
gawk: `oo' argument to `-v' not in `var=value' form
$ gawk -f script.awk -var=1 # this works and creates variable ar
$ mawk -f script.awk -var=1 # this works and creates variable ar
$ mawk -f script.awk -foo  # this fails as it expects a file oo
mawk: cannot open oo (No such file or directory)
$ gawk -f script.awk -foo  # this fails as it expects a file oo
gawk: fatal: can't open source file `oo' for reading (No such file or directory)

Наблюдение 2: ОП предлагает использовать двойной - , чтобы указать, что последовательные опции являются только частью awk.Это, однако, расширение как mawk, так и gawk, и не является частью стандарта POSIX .

--: указывает на однозначный конецопции. источник: man mawk--: сигнализировать об окончании опций.Это полезно, чтобы разрешить дальнейшие аргументы самой программе AWK начинать с -.Это обеспечивает согласованность с соглашением о разборе аргументов, используемым большинством других программ POSIX. источник: man gawk

Кроме того, использование двойного дефиса предполагает, что все аргументы после -- являются файлами:

$ ./script.awk -- -arg1 file
ARGC 3
ARG0 mawk
ARG1 -arg1
ARG2 file
mawk: cannot open -arg1 (No such file or directory)

Предложение 1: Хотя концепция флагов удобна, вы можете рассмотреть возможность использования стандартного POSIX-совместимого назначения в качестве аргументов:

$ ./script.awk arg1=1 arg2=1 arg3=1 file

Однако недостатком этого является то, что эти назначения обрабатываются только после выполнения блока BEGIN.(ср. стандарт POSIX )

Предложение 2: простое улучшение заключается в использовании ARGV и ARGC и использовании аргументов без дефисов.Это немного более похоже на BSD (cfr ps aux) и может выглядеть следующим образом:

$ ./script.awk arg1 arg2 arg3
ARGC 4
ARG0 gawk
ARG1 arg1
ARG2 arg2
ARG3 arg3

Предложение 3: Если ни один из вышеперечисленных вариантов не подходит вам,Вы должны рассмотреть возможность использования гибрида между sh и awk.Слово «гибрид» подразумевает, что мы пишем синтаксис, который распознается как sh, так и awk.Программа awk состоит из пар вида:

pattern { action }

, где pattern можно игнорировать.Это очень похоже на синтаксис составной команды sh:

{ compound-list ; }

Это позволяет нам теперь написать следующий скрипт оболочки script.sh:

#!/bin/sh
{ "awk" "-f" "$0" "--" "${@}" ; "exit" ;}
# your awk script comes here

Написав его таким образом,awk будет интерпретировать первое действие как не что иное, как объединение строк.sh с другой стороны выполнит его номинально.

К сожалению, хотя это выглядит многообещающе, это НЕ работает из-за эффекта двойного дефиса.

$ ./script.sh file   # this works
ARGC 2
ARG0 awk
ARG1 file

$ ./script.sh -arg file   # this does not work
ARGC 3
ARG0 mawk
ARG1 -arg1
ARG2 file
mawk: cannot open -arg1 (No such file or directory)

Уродливое решение может заключаться в том, чтобы начать анализировать сам скрипт, чтобы удалить первые две строки, прежде чем передать его обратно в awk.Но это решит проблему только для сценариев, имеющих только блок BEGIN.

...