Как Крис говорит , аргументы формы variablename=anything
рассматриваются как присвоение переменной (, которое выполняется во время обработки аргументов, в отличие от (более новых)-v var=value
которые выполняются перед операторами BEGIN
)вместо имен входных файлов.
Это может быть полезно в таких случаях, как:
awk '{print $1}' FS=/ RS='\n' file1 FS='\n' RS= file2
Где вы можете указать различные FS
/ RS
для каждого файла. Он также широко используется в:
awk '!file1_processed{a[$0]; next}; {...}' file1 file1_processed=1 file2
Какая версия более безопасна:
awk 'NR==FNR{a[$0]; next}; {...}' file1 file2
(который не работает, если file1
пусто)
Но это мешает, когда у вас есть файлы, имя которых содержит =
символов.
Теперь это проблема только тогда, когда то, что осталось от первого =
, является действительным awk
именем переменной.
То, что составляет допустимое имя переменной в awk
, более строго, чем в sh
.
POSIX требует, чтобы это было что-то вроде:
[_a-zA-Z][_a-zA-Z0-9]*
Только с символами переносимого набора символов. Однако /usr/xpg4/bin/awk
Solaris 11, по крайней мере, не соответствует требованиям в этом отношении и допускает использование любых буквенных символов в локали в именах переменных, а не только -zA -Z.
Таким образом, аргумент типа x+y=foo
, =bar
или ./foo=bar
по-прежнему обрабатывается как имя входного файла, а не как присваивание, поскольку то, что осталось от первого =
, не является допустимым именем переменной. Такой аргумент, как Stéphane=Chazelas.txt
, может быть, а может и не быть, в зависимости от реализации awk
и локали.
Вот почему с awk рекомендуется использовать:
awk '...'./*.txt
вместо
awk '...' *.txt
, например, чтобы избежать проблемы, если вы не можете гарантировать, что имя файлов txt
не будет содержать символы =
.
Кроме того, имейте в виду, что такой аргумент, как -vfoo=bar.txt
, может рассматриваться как вариант, если вы используете:
awk -f file.awk -vfoo=bar.txt
(также относится к awk '{code}' -vfoo=bar.txt
с awk
из busybox версий до 1.28.0, см. соответствующий отчет об ошибке).
Опять же,использование ./*.txt
работает вокруг этого (использование префикса ./
также помогает с файлом с именем -
, который в противном случае awk
понимается как значение стандартный ввод вместо ).
Вот почему
#! /usr/bin/awk -f
Шебанги на самом деле не работают. В то время как var=value
можно обойти, исправив , значения ARGV
(добавляют префикс ./
)в операторе BEGIN
:
#! /usr/bin/awk -f
BEGIN {
for (i = 1; i < ARGC; i++)
if (ARGV[i] ~ /^[_[:alpha:]][_[:alnum:]]*=/)
ARGV[i] = "./" ARGV[i]
}
# rest of awk script
Это не поможет с опциями, так как их видит скрипт awk
, а не скрипт awk
.
Одна потенциальная косметическая проблема с использованием этого префикса ./
заключается в том, что он заканчивается в FILENAME
, но вы всегда можете использовать substr(FILENAME, 3)
, чтобы удалить его, если он вам не нужен.
Реализация GNU awk
устраняет все эти проблемы с помощью опции -E
.
После -E
gawk ожидает только путь awk
скрипта (, где -
по-прежнему означает stdin ), а затем только список путей к входным файлам (, а там даже не -
обрабатывается особым образом ).
Специально разработан для:
#! /usr/bin/gawk -E
shebangs, где список аргументов всегда является входными файлами (обратите внимание, что вы по-прежнему можете редактировать этот ARGV
список в операторе BEGIN
).
Вы также можете использовать его как:
gawk -e '...awk code here...' -E /dev/null *.txt
Мы используем -E
с пустым скриптом (/dev/null
), чтобы гарантировать, что эти *.txt
последующие файлы всегда обрабатываются как входные файлы, даже если они содержат символы =
.
В большинстве версий awk аргументы после исполняемой программы::
x=y
Поскольку ваше имя файла интерпретируется как case #2, awk все еще ждет, пока что-то будет прочитано на stdin (, так как он не воспринимает, что было передано какое-либо имя файла ).
При переносе это поведение задокументировано в POSIX :
.Either of the following two types of argument can be intermixed:
- file: A pathname of a file that contains the input to be read, which is matched against the set of patterns in the program. If no file operands are specified, or if a file operand is '-', the standard input shall be used.
- assignment: An operand that begins with an underscore or alphabetic character from the portable character set (see the table in the Base Definitions volume of IEEE Std 1003.1-2001, Section 6.1, Portable Character Set), followed by a sequence of underscores, digits, and alphabetics from the portable character set, followed by the '=' character, shall specify a variable assignment rather than a pathname.
Таким образом, при переносе у вас есть несколько вариантов (#1, вероятно, наименее навязчивый):
awk..../my=file
, который обходит это, поскольку .
не является "символом подчеркивания или буквенным символом из переносимого набора символов". awk... < my=file
. Однако это не работает с несколькими файлами. ln my=file my_file
, а затем использовать my_file
как обычно. Копирование выполняться не будет, и оба файла будут поддерживаться одними и теми же данными и метаданными inode. После его использования можно безопасно удалить созданную ссылку, так как количество ссылок на индексный дескриптор все равно будет больше 0. Цитировать документация gawk(добавлено примечание):
Any additional arguments on the command line are normally treated as input files to be processed in the order specified. However, an argument that has the form var=value, assigns the value value to the variable var—it does not specify a file at all.
Почему команда останавливается и ждет? Поскольку в формеawk 'processing_script_here' my=file.txt
нет файла, указанного по приведенному выше определению,-my=file.txt
интерпретируется как присвоение переменной, и если файл не определен, awk
будет читать стандартный ввод (, что также видно из strace
, который показывает, что awk в такой команде ожидает системного вызова read(0,'...)
.
Это также задокументировано в спецификациях POSIX awk ,см. раздел ОПЕРАНДЫ и назначения часть этого)
Присвоение переменной очевидно в awk '{print foo}' foo=bar /etc/passwd
, где значение foo
печатается для каждой строки в /etc/passwd. Однако указание ./foo=bar
или полного пути работает.
Обратите внимание, что запуск strace
на awk '1' foo=bar
, а также проверка с помощью cat foo=bar
показывают, что это проблема, специфичная для awk -, и execve показывает имя файла в качестве переданного аргумента, поэтому оболочки не имеют ничего общего с переменной env. задания в этом случае.
Кроме того, обратите внимание, что awk '...script...' foo=bar
не приведет к созданию переменной среды оболочкой, поскольку назначение переменной среды должно предшествовать команде, чтобы она вступила в силу. См. Правила грамматики оболочки POSIX , пункт номер 7. Кроме того, это можно проверить с помощьюawk '{print ENVIRON["foo"]}' foo=bar /etc/passwd