awk 'processing_script_here' my=file.txt
似乎停下来并无限期地等待...
这是怎么回事,我该如何运作?
awk 'processing_script_here' my=file.txt
似乎停下来并无限期地等待...
这是怎么回事,我该如何运作?
Answers:
如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/awk
Solaris 11至少在这方面不兼容,并且允许在语言环境中使用变量名称中的任何字母字符,而不仅仅是a-zA-Z。
因此,像x+y=foo
or或=bar
or 这样的参数./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.txt
的awk
from 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,其中参数列表始终是输入文件(请注意,您仍然可以ARGV
在BEGIN
语句中自由编辑该列表)。
您也可以将其用作:
gawk -e '...awk code here...' -E /dev/null *.txt
我们使用-E
空脚本(/dev/null
)只是为了确保*.txt
之后的那些始终被视为输入文件,即使它们包含=
字符也是如此。
../foo
,/path/to/foo
以及使用不同编码的路径)-在这种情况下substr(FILENAME,3)
是不够的,或者一个简单的脚本,用户基本上可以知道文件名是什么-在这种情况下,他/他可能不应该打扰其中包含=
任何一个的文件;-)
./
问题,但是在某些情况下(例如,必须在输出中包含文件名的情况下,这可能是不可取的),在这种情况下,文件名./
应该是多余且不必要的,因此您需要以某种方式摆脱它。这是至少一个例子。至于用户知道什么是文件名-很好,在这种情况下,我们也知道什么是文件名,但=
仍然妨碍了正确的处理。领导-
也是如此。
./
而且还是全局的(绝对路径)/
,这使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上执行此操作,但在GNU / Linux上都gawk
没有mawk
执行此操作。
awk
这种方式确定的职位。
引用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
但是指定或完整路径确实有效。
请注意,在运行strace
上awk '1' foo=bar
,以及与检查cat foo=bar
表明,这是特定的awk问题,execve的不显示文件名作为参数传递,所以炮弹没有任何关系,在这种情况下,环境变量赋值。
另外,请注意awk '...script...' foo=bar
,由于环境变量分配应该在命令之前生效,因此不会导致外壳程序创建环境变量。请参阅POSIX Shell语法规则,点号7。此外,可以通过以下方式验证awk '{print ENVIRON["foo"]}' foo=bar /etc/passwd