工具sed,awk或一次perl -n处理他们的输入一条记录,默认情况下记录为行。
有些,比如awkwith RS,GNU sedwith-z或perlwith-0ooo可以通过选择不同的记录分隔符来改变记录的类型。
perl -n可以使整个输入(通过多个文件时每个单独的文件)成为带有选项的单个记录-0777(或-0后跟任何大于 0377 的八进制数,777 是规范的)。这就是他们所说的啜饮模式。
可以用awk'sRS或任何其他机制完成类似的事情吗?在哪里awk处理每个文件内容作为一个整体,而不是每个文件的每一行?
Sté*_*las 18
您可以根据是awk将其RS视为单个字符(如传统awk实现)还是作为正则表达式(likegawk或mawkdo)来采取不同的方法。空文件也很难被考虑,因为awk往往会跳过它们。
gawk,mawk或其它awk实施方案中,其中RS可以是正规表达式。在这些实现中(对于mawk,请注意某些操作系统(例如 Debian)提供了一个非常旧的版本,而不是由 @ThomasDickey 维护的现代版本),如果RS包含单个字符,则记录分隔符是该字符,或者awk在RS为空时进入段落模式,否则视为RS正则表达式。
解决方案是使用不可能匹配的正则表达式。有些人会想到x^或$x(x在开始之前或结束之后)。然而,有些(尤其是gawk)比其他的更贵。到目前为止,我发现这^$是最有效的一种。它只能匹配一个空的输入,然后就没有什么可以匹配的了。
所以我们可以这样做:
awk -v RS='^$' '{printf "%s: <%s>\n", FILENAME, $0}' file1 file2...
Run Code Online (Sandbox Code Playgroud)
但需要注意的是,它会跳过空文件(与 相反perl -0777 -n)。GNU 可以awk通过将代码放在ENDFILE语句中来解决这个问题。但是我们还需要$0在 BEGINFILE 语句中重置,否则在处理空文件后它不会被重置:
gawk -v RS='^$' '
BEGINFILE{$0 = ""}
ENDFILE{printf "%s: <%s>\n", FILENAME, $0}' file1 file2...
Run Code Online (Sandbox Code Playgroud)
awk实现,POSIXawk在那些中,RS只是一个字符,它们没有BEGINFILE/ ENDFILE,它们没有RT变量,它们通常也无法处理 NUL 字符。
你会认为 usingRS='\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
Run Code Online (Sandbox Code Playgroud)
这相当于sed's:
sed '
:1
$!{
N;b1
}
...' file1
Run Code Online (Sandbox Code Playgroud)
这种方法的另一个问题是,如果文件没有以换行符结尾(并且不为空),则仍然会$0在末尾任意添加一个(使用gawk,您可以通过使用RT而不是RS在上面的代码)。一个优点是您确实在NR/ 中记录了文件中的行数FNR。
要一次处理多个文件,一种方法是在一个BEGIN语句中手动读取所有文件(这里假设是 POSIX awk,而不是 /bin/awk带有 70 年代 API 的 Solaris ):
awk -v RS='^$' '{printf "%s: <%s>\n", FILENAME, $0}' file1 file2...
Run Code Online (Sandbox Code Playgroud)
关于尾随换行符的相同警告。那个优点是能够处理包含=字符的文件名。