使用AWK,我正在通过将文本文件拆分为多个记录来对其进行处理。作为记录分隔符RS
,我使用正则表达式。有没有一种方法可以获取找到的记录分隔符,因为RS
仅代表正则表达式字符串?
示例:
BEGIN { RS="a[0-9]*. "; ORS="\n-----\n"}
/foo/ {print $0 RS;}
END {}
输入文件:
a1. Hello
this
is foo
a2. hello
this
is bar
a3. Hello
this
is foo
输出:
Hello
this
is foo
a[0-9]*.
-----
Hello
this
is foo
a[0-9]*.
-----
如您所见,输出将RS
打印为表示正则表达式的字符串,但不打印实际值。
如何获取记录分隔符的实际匹配值?
预期输出:
Hello
this
is foo
a1
-----
Hello
this
is foo
a3
-----
答案 0 :(得分:4)
在符合POSIX的AWK中,记录分隔符RS
仅是一个字符,因此很容易以的形式回叫它。
awk 'BEGIN{RS="a"}{print $0 RS}'
另一方面, GNU AWK并不将RS
限制为一个字符的字符串,而是允许其为任何正则表达式。在这种情况下,使用上述AWK会变得有些棘手,因为RS
是正则表达式而不是字符串。
为此,GNU AWK引入了变量RT
,该变量仅代表找到的记录分隔符。当RS
是单个字符时,RT
包含相同的单个字符。但是,当RS
是正则表达式时,RT
包含与正则表达式匹配的实际输入文本。
天真的,可以将您的AWK程序更新为:
BEGIN{RS="a[0-9]+[.] "; ORS="\n-----\n"}
/foo/{print $0 RT}
不幸的是,RT
设置为在当前记录之后找到的值,并且似乎OP在当前记录之前请求了该值,因此您可以引入一个新变量pRT
,该变量可以读为找到上一个记录分隔符。
BEGIN{RS="a[0-9]+[.] "; ORS="\n-----\n"}
/foo/{print $0 pRT}{pRT=RT}
并且正如Shaki Siegal在comments中指出的那样,您仍然必须更新pRT
来删除最后的空格和点:
BEGIN{RS="a[0-9]+[.] "; ORS="\n-----\n"}
/foo/{print $0 pRT}{pRT=RT;sub(/[.] $/,"",pRT)}
注释:OP(RS
)的原始RS="a[0-9]*. "
已更新,以改进与RS="a[0-9]+[.] "
的匹配,从而确保了后面出现的数字a
和实际的.
。
如果如原始示例所示,记录分隔符始终出现在行的开头,则应将RS
稍微修改为RS="(^|\n)a[0-9]+[.] "
,Dito注释也提出了许多要点。因此,如果字符串a[0-9]+.
始终出现在开头,则您需要进行更多处理:
BEGIN {
RS ="(^|\n)a[0-9]+[.] ";
ORS="\n-----\n"
}
/foo/ {
if (RT ~ /^$/ && NR != 2) pRT = substr(pRT,2)
print $0 pRT
}
{pRT=RT;sub(/[.] $/,"",pRT)}
在这里,我们添加了更正以修复最后一条记录。
pRT
中删除第一个换行符,否则您将包含由最后一条记录引起的额外换行以换行符结尾(与其他所有行相比)。RT
不是以换行开头最终的改进是通过意识到我们始终会删除pRT
中的初始换行符,因此我们可以将其合并为一个gsub
:
BEGIN {
RS ="(^|\n)a[0-9]+[.] ";
ORS="\n-----\n"
}
/foo/ { print $0 pRT }
{pRT=RT;gsub(/^\n|[.] $/,"",pRT)}
RS
:输入记录分隔符。它的默认值是一个包含单个换行符的字符串,这意味着输入记录由一行文本组成。它也可以是空字符串,在这种情况下,记录由空白行分隔。如果是正则表达式,则记录将由输入文本中的正则表达式匹配项分隔。
RS
成为正则表达式的能力是gawk
的扩展。在大多数其他AWK实现中,或者如果gawk
处于兼容模式(请参阅选项),则仅使用RS
值的第一个字符。
ORS
::输出记录分隔符。在每个打印语句的末尾输出。它的默认值是换行符“ \ n”。
RT
:(特定于GNU AWK)与记录分隔符RS
表示的文本匹配的输入文本。每次读取记录时都会设置它。
答案 1 :(得分:1)
这可能对您有用(GNU sed):
sed -rn '/^a[0-9]+\.\s/{:a;x;/foo/{s/^(a[0-9]+\.)\s*(.*)/\2\n\1\n-----/p;$d};x;h;b};H;$ba' file
聚集以an.
开头的行,其中n是整数。如果这些行包含单词foo
,请进行必要的替换并打印结果,否则不执行任何操作。
道歉:开始解决问题时,这个问题被标记为sed
。
遇到以an.
开头的行时,该行将替换保留空间中的所有行。但是,在此之前,首先检查保留空间,如果它包含单词foo
,即已经存在一个集合,则满足要处理的要求,因此将行格式化并打印出来。其他行将追加到保留空间。当遇到文件结尾时,将满足特殊条件,该条件与行开始an.
时的条件相同。这可以通过添加goto标签:a
来实现。
答案 2 :(得分:1)
对于已经用于多字符RS的GNU awk,包含与RS regexp匹配的字符串的内置变量为RT
。
尽管我们需要修复您的RS设置,因为您需要RS的正则表达式在行首(a<integer><dot><blank>
)匹配(^|\n)a[0-9]+[.]
或在文件末尾单独换行(\n$
),因此文件中的最后一条记录与所有其余记录的解析方式相同,下面是如何写入的记录。请注意,除了文件中的第一个匹配项外,RT都将以换行符开头,因此我们需要从RT中删除该前导换行符,以获取要为每条记录打印的实际标识符:
$ cat tst.awk
BEGIN {
RS = "(^|\n)a[0-9]+[.] |\n$"
ORS = "\n-----\n"
}
/foo/ { print $0 "\n" id }
{ id = gensub(/^\n|[.] /,"","g",RT) }
这是给定此输入的结果,其中包括比问题多的雨天案例(您应对此进行测试其他提议的解决方案):
输入:
$ cat file
a1. Hello
this
is foo bat man
a2. hello
this
is bar
a3. Hello
this is a7. just fine
is foo
输出:
$ awk -f tst.awk file
Hello
this
is foo bat man
a1
-----
Hello
this is a7. just fine
is foo
a3
-----