在AWK中使用Slurp模式?


16

诸如之类的工具sedawk或一次perl -n处理其输入一个记录,默认情况下,记录

某些诸如awkwith RS,GNU sedwith -zperlwith -0ooo可以通过选择其他记录分隔符来更改记录的类型。

perl -n可以使整个输入(通过多个文件传递时每个文件)成为带有选项的单个记录-0777(或-0后跟任何大于0377的八进制数,即777为规范值)。这就是他们所说的“ 灌浆模式”

可类似这样完成awkRS或任何其他机制?在哪里按顺序awk处理每个文件内容,而不是按顺序处理每个文件的每一

Answers:


15

您可以采用不同的方法,具体取决于awk将其RS视为单个字符(如传统awk实现那样)还是将其视为正则表达式(如gawkmawk做)。空文件也很棘手,因为awk它们往往会跳过它们。

gawkmawk或其它awk实施方案中,其中RS可以是正规表达式。

在这些实现中(对于mawk,请注意,像Debian之类的某些OS会发布非常旧的版本,而不是由@ThomasDickey维护的现代版本),如果RS包含单个字符,则记录分隔符为该字符,或者为空awk时进入段落模式RS,否则将其RS视为正则表达式。

解决方案是使用无法匹配的正则表达式。有些人想到x^$xx在开始之前或结束之后)。但是,某些(尤其是带有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


至于最后一部分(“如果文件不是以换行符结尾(并且不为空),则仍会在$ 0处任意添加结尾”):对于文本文件,它们应该以结尾结尾新队。例如,vi添加了一个,因此在保存文件时对其进行了修改。没有终止的换行符会使某些命令放弃最后的“行”(例如:wc),但其他命令仍“看到”最后一行... ymmv。因此,如果您应该处理文本文件,那么您的解决方案就是有效的,imo(可能是这种情况,因为awk对文本处理有利,但对二进制文件^^不利)
Olivier Dulac

1
试图吞吃所有内容可能会遇到一些限制...传统的awk显然在一行​​中有(必须有)99个字段的限制...所以您可能还需要使用其他FS来避免该限制,但是您可能在一条线的总长度(或整件事,如果您设法将全部都放在一条线上)上有多长的限制?
Olivier Dulac

最终:(一个愚蠢的...)黑客可能是第一个解析整个文件并查找其中不存在的字符,然后tr '\n' 'thatchar' 在将该文件发送到awk之前查找文件,然后 tr 'thatchar' \n'输出?(您可能仍需要添加换行符,以确保您的输入文件具有终止的换行符,如上文所述:( { tr '\n' 'missingchar' < thefile ; printf "\n" ;} | awk ..... | { tr 'missingchar' '\n' }但最后要添加“ \ n”,可能需要删除...)如果最后的tr在不终止换行符的情况下接受文件,则在最终tr之前添加sed ...)
Olivier Dulac

@OlivierDulac,仅在我们访问NF或任何字段时才会达到字段数限制。awk如果我们不这样做,则不进行拆分。话虽这么说,甚至/bin/awkSolaris 9(基于1970年代的awk)也没有这个限制,所以我不确定我们能找到一个可以做到的限制(因为SVR4的oawk的限制为99和nawk 199,所以仍然可以实现)可能是由Sun增加了该限制的解除,并且可能在其他基于SVR4的awks中找不到,您可以在AIX上进行测试吗?)。
斯特凡Chazelas
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.