您可以采用不同的方法,具体取决于awk
将其RS
视为单个字符(如传统awk
实现那样)还是将其视为正则表达式(如gawk
或mawk
做)。空文件也很棘手,因为awk
它们往往会跳过它们。
gawk
,mawk
或其它awk
实施方案中,其中RS
可以是正规表达式。
在这些实现中(对于mawk
,请注意,像Debian之类的某些OS会发布非常旧的版本,而不是由@ThomasDickey维护的现代版本),如果RS
包含单个字符,则记录分隔符为该字符,或者为空awk
时进入段落模式RS
,否则将其RS
视为正则表达式。
解决方案是使用无法匹配的正则表达式。有些人想到x^
或$x
(x
在开始之前或结束之后)。但是,某些(尤其是带有gawk
)比其他更昂贵。到目前为止,我发现这^$
是最有效的一种。它只能在空输入上匹配,但是将没有匹配的对象。
所以我们可以做:
awk -v RS='^$' '{printf "%s: <%s>\n", FILENAME, $0}' file1 file2...
需要注意的是,它会跳过空文件(与相反perl -0777 -n
)。GNU可以awk
通过将代码放在ENDFILE
语句中来解决。但是我们还需要$0
在BEGINFILE语句中进行重置,因为否则在处理空文件后将不会重置该语句:
gawk -v RS='^$' '
BEGINFILE{$0 = ""}
ENDFILE{printf "%s: <%s>\n", FILENAME, $0}' file1 file2...
传统awk
实现,POSIXawk
在这些中,RS
只有一个角色,他们没有BEGINFILE
/ ENDFILE
,没有RT
变量,它们通常也不能处理NUL字符。
您可能会认为使用RS='\0'
可以正常工作,因为无论如何他们都无法处理包含NUL字节的输入,但是不可以,RS='\0'
在传统实现中将RS=
其视为,即段落模式。
一种解决方案是使用不太可能在输入中找到的字符,例如 \1
。在多字节字符语言环境中,您甚至可以使其成为字节序列,因为它们形成了未分配的字符或非字符(例如$'\U10FFFE'
在UTF-8语言环境中),因此极不可能发生。但是,这并不是很简单,而且空文件也有问题。
另一种解决方案是将整个输入存储在变量中,然后在END语句中进行处理。这意味着您一次只能处理一个文件:
awk '{content = content $0 RS}
END{$0 = content
printf "%s: <%s>\n", FILENAME, $0
}' file
等同于sed
:
sed '
:1
$!{
N;b1
}
...' file1
这种方法的另一个问题是,如果文件不是以换行符结尾(并且不为空),则仍会$0
在末尾任意添加一个文件(使用gawk
,您可以使用RT
而不是RS
在上面的代码)。一个优点是您确实在NR
/中记录了文件中的行数FNR
。