awk 'processing_script_here' my=file.txt
кажется, останавливается и ждет бесконечно ...
Что здесь происходит и как я могу заставить его работать?
awk 'processing_script_here' my=file.txt
кажется, останавливается и ждет бесконечно ...
Что здесь происходит и как я могу заставить его работать?
Ответы:
Как говорит Крис , аргументы формы 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, по крайней мере, не соответствует этим требованиям и допускает любые алфавитные символы в локали в именах переменных, а не только a-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
с пустым script ( /dev/null
) просто для того, чтобы *.txt
впоследствии убедиться, что эти потом всегда обрабатываются как входные файлы, даже если они содержат =
символы.
../foo
, /path/to/foo
и пути, которые находятся в другой кодировке) - в этом случае substr(FILENAME,3)
будет недостаточно, или это сценарий с одним выстрелом, где пользователь в основном знает, что такое имена файлов - в этом случае он / она, вероятно, не должен беспокоиться ни о одном из них, содержащем =
либо ;-)
./
это проблема, но это может быть нежелательно при определенных условиях, таких как случаи, когда имя файла должно быть включено в вывод, и в этом случае ./
должно быть избыточным и ненужным, так что вы Нужно как-то от этого избавиться. Вот хотя бы один пример . Что касается пользователя, который знает, что такое имена файлов - хорошо, в этом случае мы также знаем, что такое имя файла, но =
все равно мешает правильной обработке. Так может помешать ведущему -
.
./
префикс, чтобы обойти эту awk
(неправильную) функцию, но затем вы получите то, что ./
на выходе, которое вы можете удалить. Посмотрите, как проверить, содержит ли первая строка файла определенную строку? В качестве примера.
./
но и глобальным (абсолютный путь), /
который заставляет awk интерпретировать аргумент как файл.
В большинстве версий awk аргументы после выполняемой программы:
x=y
Поскольку ваше имя файла интерпретируется как случай №2, awk все еще ожидает что-то, что можно прочитать на stdin (так как он не видит, что какое-либо имя файла было передано).
Это поведение задокументировано в POSIX :
Любой из следующих двух типов аргументов может быть смешан:
- file: путь к файлу, который содержит входные данные для чтения, которые сопоставляются с набором шаблонов в программе. Если файловые операнды не указаны, или если файловый операнд имеет значение «-», должен использоваться стандартный ввод.
- назначение: операнд, который начинается с символа подчеркивания или алфавита из переносимого набора символов (см. таблицу в томе Базовых определений стандарта IEEE Std 1003.1-2001, раздел 6.1, переносимый набор символов), за которым следует последовательность подчеркиваний, цифр, и алфавит из переносимого набора символов, за которым следует символ «=», должен определять присвоение переменной, а не имя пути.
Таким образом, переносимо, у вас есть несколько вариантов (# 1, вероятно, наименее навязчивый):
awk ... ./my=file
, которое обходит это, поскольку .
не является "подчеркиванием или буквенным символом из переносимого набора символов".awk ... < my=file
. Тем не менее, это не работает хорошо с несколькими файлами.ln my=file my_file
, а затем использовать my_file
как обычно. Копирование не будет выполняться, и оба файла будут поддерживаться одними и теми же данными и метаданными inode. После его использования можно безопасно удалить созданную ссылку, поскольку количество ссылок на индекс будет по-прежнему больше 0../my=file
работает? % awk 'processing_script_here' ./my=file.txt awk: fatal: cannot open file ./my=file.txt' for reading (No such file or directory).
Это должно быть переносимым, потому что ./my
не является допустимым именем переменной, поэтому не должно быть проанализировано таким образом.
=
следует знак подчеркивания или алфавитный символ из переносимого набора символов (см. Таблицу в разделе «Базовые определения» стандарта IEEE Std 1003.1-2001, раздел 6.1, Переносимый набор символов), затем следует последовательность символов подчеркивания, цифр и алфавита из переносимого набора символов . Таким образом, путь к файлу, как ++foo=bar.txt
или =foo
или ./foo=bar
все в порядке, .
или +
не является [_a-zA-Z]
.
./my=file
будет передано через дословно.
awk '{print $1,$2}' /etc/passwd
. Дело в том, что наличие оболочки, открывающей файл, в отличие от awk, не имеет никакого значения, делает ли его доступным для поиска или нет. На самом деле, awk '{exit}' < /etc/passwd
вы можете ожидать awk
возврата к концу первой записи, exit
чтобы убедиться, что он оставит там позицию в stdin. POSIX требует этого. /usr/xpg4/bin/awk
делает это на Solaris, но, похоже, gawk
ни mawk
на GNU / Linux.
awk
таким образом.
Чтобы процитировать документацию gawk (примечание выделено):
Любые дополнительные аргументы в командной строке обычно обрабатываются как входные файлы для обработки в указанном порядке. Однако аргумент, который имеет форму var = value, присваивает значение value переменной var - он вообще не указывает файл.
Почему команда останавливается и ждет? Потому что в форме awk 'processing_script_here' my=file.txt
нет файла, указанного в приведенном выше определении - my=file.txt
интерпретируется как присвоение переменной, и, если файл не определен, awk
будет читать stdin (также видно, из strace
чего видно, что awk в такой команде ожидает read(0,'...)
syscall.
Это также задокументировано в спецификации POSIX awk , см. Раздел ОПЕРАНДЫ и часть назначений )
Назначение переменной очевидно в awk '{print foo}' foo=bar /etc/passwd
том смысле, что значение foo
выводится для каждой строки в / etc / passwd. Однако указание ./foo=bar
или полный путь работает.
Обратите внимание , что работает strace
на awk '1' foo=bar
, а также проверки с cat foo=bar
показывает , что это AWK-специфическая проблема, и execve делает шоу имени файла в качестве аргумента передается, поэтому снаряды не имеют ничего общего с переменным окр заданий в этом случае.
Кроме того, обратите внимание, что awk '...script...' foo=bar
это не приведет к созданию переменной окружения с помощью оболочки, так как назначение переменных окружения должно предшествовать команде для вступления в силу. См. Правила грамматики оболочки POSIX , пункт № 7. Кроме того, это можно проверить с помощьюawk '{print ENVIRON["foo"]}' foo=bar /etc/passwd