Как говорит Крис , аргументы формы variablename=anythingобрабатываются как присваивание переменных (которые выполняются в то время, когда аргументы обрабатываются в отличие от (более новых) -v var=value, которые выполняются до BEGINоператоров) вместо имен входных файлов.
Но это мешает, когда у вас есть файлы, имя которых содержит =символы.
Теперь проблема только в том, что от первого =остается правильное awkимя переменной.
То, что составляет допустимое имя переменной в, awkявляется более строгим, чем в sh.
POSIX требует, чтобы это было что-то вроде:
[_a-zA-Z][_a-zA-Z0-9]*
Только с символами переносимого набора символов. Однако /usr/xpg4/bin/awkSolaris 11, по крайней мере, не соответствует этим требованиям и допускает любые алфавитные символы в локали в именах переменных, а не только a-zA-Z.
Таким образом, аргумент, подобный x+y=fooили =barили ./foo=bar, все еще рассматривается как имя входного файла, а не как присваивание, поскольку то, что осталось от первого =, не является допустимым именем переменной. Аргумент типа Stéphane=Chazelas.txtможет или не может, в зависимости от awkреализации и локали.
Вот почему с awk рекомендуется использовать:
awk '...'./*.txt
вместо того
awk '...'*.txt
например, чтобы избежать проблемы, если вы не можете гарантировать, что имя txtфайла не будет содержать =символов.
Также помните, что аргумент, подобный аргументу, -vfoo=bar.txtможет рассматриваться как опция, если вы используете:
Опять же, использование ./*.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впоследствии убедиться, что эти потом всегда обрабатываются как входные файлы, даже если они содержат =символы.
Я не вижу, как явный путь, заканчивающийся в FILENAME, является проблемой. Либо сценарий awk является общим, и в этом случае он должен обрабатывать все виды путей, заканчивающихся в FILENAME (включая, но не ограничиваясь ../foo, /path/to/fooи пути, которые находятся в другой кодировке) - в этом случае substr(FILENAME,3)будет недостаточно, или это сценарий с одним выстрелом, где пользователь в основном знает, что такое имена файлов - в этом случае он / она, вероятно, не должен беспокоиться ни о одном из них, содержащем =либо ;-)
mosvy
2
@mosvy Я не думаю, что в нем так много говорится о том, что ./это проблема, но это может быть нежелательно при определенных условиях, таких как случаи, когда имя файла должно быть включено в вывод, и в этом случае ./должно быть избыточным и ненужным, так что вы Нужно как-то от этого избавиться. Вот хотя бы один пример . Что касается пользователя, который знает, что такое имена файлов - хорошо, в этом случае мы также знаем, что такое имя файла, но =все равно мешает правильной обработке. Так может помешать ведущему -.
Сергей Колодяжный
@mosvy, да, идея в том, что вы хотите использовать ./префикс, чтобы обойти эту awk(неправильную) функцию, но затем вы получите то, что ./на выходе, которое вы можете удалить. Посмотрите, как проверить, содержит ли первая строка файла определенную строку? В качестве примера.
Стефан Шазелас
Является не только локальным (относительно этого каталога), ./но и глобальным (абсолютный путь), /который заставляет awk интерпретировать аргумент как файл.
Исаак
21
В большинстве версий awk аргументы после выполняемой программы:
Файл
Назначение формы x=y
Поскольку ваше имя файла интерпретируется как случай №2, awk все еще ожидает что-то, что можно прочитать на stdin (так как он не видит, что какое-либо имя файла было передано).
Любой из следующих двух типов аргументов может быть смешан:
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не является допустимым именем переменной, поэтому не должно быть проанализировано таким образом.
Стивен Харрис
2
Как говорится в этом тексте POSIX, проблема заключается только в том, что перед первым =следует знак подчеркивания или алфавитный символ из переносимого набора символов (см. Таблицу в разделе «Базовые определения» стандарта IEEE Std 1003.1-2001, раздел 6.1, Переносимый набор символов), затем следует последовательность символов подчеркивания, цифр и алфавита из переносимого набора символов . Таким образом, путь к файлу, как ++foo=bar.txtили =fooили ./foo=barвсе в порядке, .или +не является [_a-zA-Z].
Стефан Шазелас
1
@SergiyKolodyazhnyy awk является внешним по отношению к оболочке, поэтому не имеет значения, какой вы используете. ./my=fileбудет передано через дословно.
Крис Даун
1
@SergiyKolodyazhnyy, то же самое для awk '{print $1,$2}' /etc/passwd. Дело в том, что наличие оболочки, открывающей файл, в отличие от awk, не имеет никакого значения, делает ли его доступным для поиска или нет. На самом деле, awk '{exit}' < /etc/passwdвы можете ожидать awkвозврата к концу первой записи, exitчтобы убедиться, что он оставит там позицию в stdin. POSIX требует этого. /usr/xpg4/bin/awkделает это на Solaris, но, похоже, gawkни mawkна GNU / Linux.
Стефан Шазелас
3
@mosvy, см. раздел « ВХОДНЫЕ ФАЙЛЫ » на pubs.opengroup.org/onlinepubs/9699919799/utilities/… Это полезно для ряда шаблонов использования, которые имеют смысл только для обычных файлов, например, когда вы хотите обрезать файл или записать в него данные в позиция, идентифицированная awkтаким образом.
Любые дополнительные аргументы в командной строке обычно обрабатываются как входные файлы для обработки в указанном порядке. Однако аргумент, который имеет форму 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
Ответы:
Как говорит Крис , аргументы формы
variablename=anything
обрабатываются как присваивание переменных (которые выполняются в то время, когда аргументы обрабатываются в отличие от (более новых)-v var=value
, которые выполняются доBEGIN
операторов) вместо имен входных файлов.Это может быть полезно в таких вещах, как:
Где вы можете указать другой
FS
/RS
для файла. Это также обычно используется в:Какая версия безопаснее:
(который не работает, если
file1
пуст)Но это мешает, когда у вас есть файлы, имя которых содержит
=
символы.Теперь проблема только в том, что от первого
=
остается правильноеawk
имя переменной.То, что составляет допустимое имя переменной в,
awk
является более строгим, чем вsh
.POSIX требует, чтобы это было что-то вроде:
Только с символами переносимого набора символов. Однако
/usr/xpg4/bin/awk
Solaris 11, по крайней мере, не соответствует этим требованиям и допускает любые алфавитные символы в локали в именах переменных, а не только a-zA-Z.Таким образом, аргумент, подобный
x+y=foo
или=bar
или./foo=bar
, все еще рассматривается как имя входного файла, а не как присваивание, поскольку то, что осталось от первого=
, не является допустимым именем переменной. Аргумент типаStéphane=Chazelas.txt
может или не может, в зависимости отawk
реализации и локали.Вот почему с awk рекомендуется использовать:
вместо того
например, чтобы избежать проблемы, если вы не можете гарантировать, что имя
txt
файла не будет содержать=
символов.Также помните, что аргумент, подобный аргументу,
-vfoo=bar.txt
может рассматриваться как опция, если вы используете:(также относится и к
awk '{code}' -vfoo=bar.txt
сawk
от версии BusyBox до 1.28.0, см соответствующего сообщения об ошибке ).Опять же, использование
./*.txt
обходится без этого (использование./
префикса также помогает с файлом с именем,-
который в противном случаеawk
понимается как означающий стандартный ввод ).Вот почему
шебанги на самом деле не работают. В то время как
var=value
те , можно обойти путем фиксации наARGV
значения (добавить./
префикс) вBEGIN
заявлении:Это не поможет с опциональными, так как они видны самим сценарием,
awk
а не самимawk
сценарием.Одна потенциальная косметическая проблема с использованием этого
./
префикса заключается в том, что он заканчиваетсяFILENAME
, но вы всегда можете использовать егоsubstr(FILENAME, 3)
для удаления, если не хотите.Реализация GNU
awk
исправляет все эти проблемы с помощью-E
опции.После
-E
этого gawk ожидает только путь кawk
сценарию (где-
все еще означает stdin), а затем список только путей к входным файлам (и там, даже-
не обрабатывается специально).Он специально разработан для:
shebangs, где список аргументов всегда является входными файлами (обратите внимание, что вы все еще можете редактировать этот
ARGV
список вBEGIN
выражении).Вы также можете использовать его как:
Мы используем
-E
с пустым script (/dev/null
) просто для того, чтобы*.txt
впоследствии убедиться, что эти потом всегда обрабатываются как входные файлы, даже если они содержат=
символы.источник
../foo
,/path/to/foo
и пути, которые находятся в другой кодировке) - в этом случаеsubstr(FILENAME,3)
будет недостаточно, или это сценарий с одним выстрелом, где пользователь в основном знает, что такое имена файлов - в этом случае он / она, вероятно, не должен беспокоиться ни о одном из них, содержащем=
либо ;-)./
это проблема, но это может быть нежелательно при определенных условиях, таких как случаи, когда имя файла должно быть включено в вывод, и в этом случае./
должно быть избыточным и ненужным, так что вы Нужно как-то от этого избавиться. Вот хотя бы один пример . Что касается пользователя, который знает, что такое имена файлов - хорошо, в этом случае мы также знаем, что такое имя файла, но=
все равно мешает правильной обработке. Так может помешать ведущему-
../
префикс, чтобы обойти этуawk
(неправильную) функцию, но затем вы получите то, что./
на выходе, которое вы можете удалить. Посмотрите, как проверить, содержит ли первая строка файла определенную строку? В качестве примера../
но и глобальным (абсолютный путь),/
который заставляет awk интерпретировать аргумент как файл.В большинстве версий awk аргументы после выполняемой программы:
x=y
Поскольку ваше имя файла интерпретируется как случай №2, awk все еще ожидает что-то, что можно прочитать на stdin (так как он не видит, что какое-либо имя файла было передано).
Это поведение задокументировано в POSIX :
Таким образом, переносимо, у вас есть несколько вариантов (# 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 (примечание выделено):
Почему команда останавливается и ждет? Потому что в форме
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
источник