使用 Gnu AWK 检索匹配的正则表达式记录分隔符

Gou*_*rav 2 awk

使用 AWK,我通过将文本文件拆分为多个记录来处理该文本文件。我使用正则表达式作为记录分隔符RS。有没有办法获取RS仅代表正则表达式字符串的找到的记录分隔符?

例子:

BEGIN { RS="a[0-9]*. "; ORS="\n-----\n"}
  /foo/ {print $0 RS;}
END {}
Run Code Online (Sandbox Code Playgroud)

输入文件:

a1. Hello
this
is foo
a2. hello
this
is bar
a3. Hello
this
is foo
Run Code Online (Sandbox Code Playgroud)

输出:

Hello
this
is foo
a[0-9]*.
-----
Hello
this
is foo
a[0-9]*.
-----
Run Code Online (Sandbox Code Playgroud)

如您所见,输出打印RS为表示正则表达式的字符串,但不打印实际值。如何检索记录分隔符的实际匹配值?

预期输出:

Hello
this
is foo
a1
-----
Hello
this
is foo
a3
-----
Run Code Online (Sandbox Code Playgroud)

kva*_*our 5

在符合 POSIX 标准的 AWK 中,记录分隔符RS只是一个字符,因此很容易以 的形式调用它。

\n
awk \'BEGIN{RS="a"}{print $0 RS}\'\n
Run Code Online (Sandbox Code Playgroud)\n

另一方面,GNU AWK 不限RS于单字符字符串,而是允许它是任何正则表达式。在这种情况下,使用上面的 AWK 会变得有点棘手,因为RS是正则表达式而不是字符串。

\n

为此,GNU AWK 引入了变量RT,该变量仅代表找到的记录分隔符。当RS是单个字符时,RT包含相同的单个字符。但是,whenRS是正则表达式,RT包含与正则表达式匹配的实际输入文本。

\n

如此天真地,人们可以将您的 AWK 程序更新为:

\n
BEGIN{RS="a[0-9]+[.] "; ORS="\\n-----\\n"}\n/foo/{print $0 RT}\n
Run Code Online (Sandbox Code Playgroud)\n

不幸的是,RT被设置为当前记录之后找到的值,并且OP似乎请求当前记录之前的值,因此您可以引入一个新变量,pRT该变量可以被读取为发现的先前记录分隔符

\n
BEGIN{RS="a[0-9]+[.] "; ORS="\\n-----\\n"}\n/foo/{print $0 pRT}{pRT=RT}\n
Run Code Online (Sandbox Code Playgroud)\n

正如Shaki Siegal在评论中指出的那样,您仍然需要更新pRT以删除最后的空格和点:

\n
BEGIN{RS="a[0-9]+[.] "; ORS="\\n-----\\n"}\n/foo/{print $0 pRT}{pRT=RT;sub(/[.] $/,"",pRT)}\n
Run Code Online (Sandbox Code Playgroud)\n

注意:RS OP ( ) 的原始版本RS="a[0-9]*. "已更新,以改进与 的匹配。RS="a[0-9]+[.] "这确保了后面的数字a和实际的的出现.

\n

如果,如原始示例所示,记录分隔符始终出现在行的开头,RS则应稍微修改为RS="(^|\\n)a[0-9]+[.] "Dito 注释也提出了各种精彩的观点。因此,如果字符串a[0-9]+. 总是出现在开头,则需要进行更多处理:

\n
BEGIN {\n   RS ="(^|\\n)a[0-9]+[.] ";\n   ORS="\\n-----\\n"\n}\n/foo/ {\n   if (RT ~ /^$/ && NR != 2) pRT = substr(pRT,2)\n   print $0 pRT \n}\n{pRT=RT;sub(/[.] $/,"",pRT)}\n
Run Code Online (Sandbox Code Playgroud)\n

在这里,我们添加了一个更正来修复最后一条记录。

\n
    \n
  • 如果有两个以上的 AWK 记录(第一条记录始终为空),则需要从 中删除第一个换行符pRT,否则您将包含由最后一条以换行符结尾的记录引起的额外换行符 (与所有其他人相反)。
  • \n
  • 如果只有两个 AWK 记录(一个在文本中有效),那么您不应该执行此更正,因为第一个记录RT不以换行符开头
  • \n
\n

最后的改进是通过意识到我们总是删除初始换行符(pRT如果存在)来完成的,因此我们可以将其全部合并到一个中gsub

\n
BEGIN {\n   RS ="(^|\\n)a[0-9]+[.] ";\n   ORS="\\n-----\\n"\n}\n/foo/ { print $0 pRT }\n{pRT=RT;gsub(/^\\n|[.] $/,"",pRT)}\n
Run Code Online (Sandbox Code Playgroud)\n
\n
\n

RS输入记录分隔符。它的默认值是包含单个换行符的字符串,这意味着输入记录由单行文本组成。它也可以是空字符串,在这种情况下,记录由空行分隔。如果它是正则表达式,则记录由输入文本中正则表达式的匹配项分隔。

\n

RS成为正则表达式的能力是一种gawk扩展。在大多数其他 AWK 实现中,或者如果gawk处于兼容模式(请参阅选项),则仅RS使用 \xe2\x80\x99s 值的第一个字符。

\n

ORS输出记录分隔符。它在每个打印语句的末尾输出。它的默认值为“\\n”,即换行符。

\n

RTRS (GNU AWK 特定)与记录分隔符表示的文本匹配的输入文本。每次读取记录时都会设置它。

\n

来源:GNU AWK 手册

\n
\n