如果文件名包含=,awk为什么会停止并等待,以及如何解决呢?


Answers:


19

Chris所说,形式的参数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至少在这方面不兼容,并且允许在语言环境中使用变量名称中的任何字母字符,而不仅仅是a-zA-Z。

因此,像x+y=fooor或=baror 这样的参数./foo=bar仍被视为输入文件名,而不是赋值,因为第=一个参数的剩余名称不是有效的变量名。Stéphane=Chazelas.txt根据awk实现和语言环境,可能会或可能不会的参数。

因此,建议使用awk:

awk '...' ./*.txt

代替

awk '...' *.txt

例如,如果您不能保证txt文件名不包含=字符,则可以避免该问题。

另外,请注意,-vfoo=bar.txt如果您使用以下参数,则可能会将类似的参数视为选项:

awk -f file.awk -vfoo=bar.txt

(也适用于1.28.0之前awk '{code}' -vfoo=bar.txtawkfrom busybox版本,请参见相应的错误报告)。

再次,使用./*.txt解决方法(使用./前缀也有助于名为的文件-,否则该文件将被awk理解为标准输入)。

那也是为什么

#! /usr/bin/awk -f

shebangs并没有真正起作用。而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脚本的路径(此处-仍表示标准输入),然后仅期望输入文件路径的列表(在那里,甚至-没有被特别对待)。

专为以下目的而设计:

#! /usr/bin/gawk -E

shebangs,其中参数列表始终是输入文件(请注意,您仍然可以ARGVBEGIN语句中自由编辑该列表)。

您也可以将其用作:

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

我们使用-E空脚本(/dev/null)只是为了确保*.txt之后的那些始终被视为输入文件,即使它们包含=字符也是如此。


我看不到以FILENAME结尾的显式路径是怎么回事。要么awk脚本是通用的,在这种情况下,它应该处理所有以FILENAME结尾的路径(包括但不限于../foo/path/to/foo以及使用不同编码的路径)-在这种情况下substr(FILENAME,3)是不够的,或者一个简单的脚本,用户基本上可以知道文件名是什么-在这种情况下,他/他可能不应该打扰其中包含=任何一个的文件;-)
mosvy 18/12/23

2
@mosvy我认为它并没有说明太多./问题,但是在某些情况下(例如,必须在输出中包含文件名的情况下,这可能是不可取的),在这种情况下,文件名./应该是多余且不必要的,因此您需要以某种方式摆脱它。这是至少一个例子。至于用户知道什么是文件名-很好,在这种情况下,我们也知道什么是文件名,但=仍然妨碍了正确的处理。领导-也是如此。
Sergiy Kolodyazhnyy

@mosvy,是的,这个想法是,您想使用./前缀来解决该awk(错误)功能,但是最终./在输出中出现一个that ,您可能想要删除它。查看如何检查文件的第一行是否包含特定字符串?举个例子。
斯特凡Chazelas

它不仅是本地的(相对于此目录),./而且还是全局的(绝对路径)/,这使awk将参数解释为文件。
以撒

21

在大多数版本的awk中,要执行的程序之后的参数为:

  1. 一份文件
  2. 表格的分配 x=y

由于您的文件名被解释为案例2,因此awk仍在等待在stdin上读取内容(因为它不认为已经传递了任何文件名)。

可移植地,此行为记录在POSIX中

以下两种类型的参数中的任何一种都可以混合使用:

  • file:包含要读取的输入的文件的路径名,该路径名与程序中的模式集匹配。如果未指定文件操作数,或者文件操作数为“-”,则应使用标准输入。
  • 赋值:操作数,以可移植字符集中的下划线或字母字符开头(请参阅IEEE Std 1003.1-2001的“基本定义”卷中的表,第6.1节,可移植字符集),然后是下划线,数字,和来自便携式字符集的字母,后跟“ =”字符,应指定变量分配而不是路径名。

因此,可移植的是,您有几个选择(#1可能是侵入性最低的):

  1. 使用awk ... ./my=file,这可以避免,因为.它不是“可移植字符集中的下划线或字母字符”。
  2. 使用将文件放在stdin上awk ... < my=file。但是,这不适用于多个文件。
  3. 临时建立到文件的硬链接,然后使用它。您可以执行类似的操作ln my=file my_file,然后my_file照常使用。将不会执行复制,并且两个文件都将由相同的数据和inode元数据支持。使用它之后,可以安全地删除创建的链接,因为对索引节点的引用数仍将大于0。

6
不行./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它不是有效的变量名,因此不应以这种方式进行解析。
Stephen Harris

2
就像POSIX文本所说的那样,问题仅在于当第一个字符=前面带有可移植字符集的下划线或字母字符时(请参阅IEEE Std 1003.1-2001基本定义卷第6.1节“可移植字符集”中的表),然后是可移植字符集中的下划线,数字和字母序列。因此喜欢的文件路径++foo=bar.txt=foo或者./foo=bar是都可以作为.+不是[_a-zA-Z]
斯特凡Chazelas

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上执行此操作,但在GNU / Linux上都gawk没有mawk执行此操作。
斯特凡Chazelas

3
@mosvy,请输入文件的部分pubs.opengroup.org/onlinepubs/9699919799/utilities/...它在许多的使用模式,才有意义与普通文件,当您想在截断文件或写数据到它像有用通过awk这种方式确定的职位。
斯特凡Chazelas

3

引用gawk文档(已添加注释重点):

命令行上的任何其他自变量通常都被视为要按指定顺序处理的输入文件。但是,具有var = 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但是指定或完整路径确实有效。

请注意,在运行straceawk '1' foo=bar,以及与检查cat foo=bar表明,这是特定的awk问题,execve的不显示文件名作为参数传递,所以炮弹没有任何关系,在这种情况下,环境变量赋值。

另外,请注意awk '...script...' foo=bar,由于环境变量分配应该在命令之前生效,因此不会导致外壳程序创建环境变量。请参阅POSIX Shell语法规则,点号7。此外,可以通过以下方式验证awk '{print ENVIRON["foo"]}' foo=bar /etc/passwd

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.