Почему awk остановится и Подождите, если файльное имя содержится = и как обойти это работать?

Альтернативное решение sed :

sed 's/(.*//' file
25
22.12.2018, 22:44
3 ответа

Как Крис говорит , аргументы формы 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/awkSolaris 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.

После -Egawk ожидает только путь awkскрипта (, где -по-прежнему означает stdin ), а затем только список путей к входным файлам (, а там даже не -обрабатывается особым образом ).

Специально разработан для:

#! /usr/bin/gawk -E

shebangs, где список аргументов всегда является входными файлами (обратите внимание, что вы по-прежнему можете редактировать этот ARGVсписок в операторе BEGIN).

Вы также можете использовать его как:

gawk -e '...awk code here...' -E /dev/null *.txt

Мы используем -Eс пустым скриптом (/dev/null), чтобы гарантировать, что эти *.txtпоследующие файлы всегда обрабатываются как входные файлы, даже если они содержат символы =.

21
27.01.2020, 19:40

В большинстве версий awk аргументы после исполняемой программы::

  1. Файл
  2. Присвоение вида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, вероятно, наименее навязчивый):

  1. Используйте awk..../my=file, который обходит это, поскольку .не является "символом подчеркивания или буквенным символом из переносимого набора символов".
  2. Поместите файл на стандартный ввод с помощью awk... < my=file. Однако это не работает с несколькими файлами.
  3. Временно создайте жесткую ссылку на файл и используйте ее. Вы можете сделать что-то вроде ln my=file my_file, а затем использовать my_fileкак обычно. Копирование выполняться не будет, и оба файла будут поддерживаться одними и теми же данными и метаданными inode. После его использования можно безопасно удалить созданную ссылку, так как количество ссылок на индексный дескриптор все равно будет больше 0.
22
27.01.2020, 19:40

Цитировать документация 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

3
27.01.2020, 19:40

Теги

Похожие вопросы