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

使用Gnu AWK检索匹配的正则表达式记录分隔符,awk,Awk,使用AWK,我通过将文本文件拆分为多个记录来处理它。作为记录分隔符,我使用正则表达式。有没有办法获得找到的记录分隔符,因为RS只表示正则表达式字符串 例如: 输入文件: 输出: 如您所见,输出将RS打印为表示正则表达式的字符串,但不打印实际值。 如何检索记录分隔符的实际匹配值 预期产出: 在POSIX兼容的AWK中,记录分隔符RS只是一个字符,因此很容易以字符的形式调用它 awk 'BEGIN{RS="a"}{print $0 RS}' 另一方面,GNU AWK不限制RS为

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

例如:

输入文件:

输出:

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

预期产出:


在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,该变量可以被读取为找到的上一个记录分隔符

正如中所指出的,您仍然需要更新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=^ |\na[0-9]+[。]Dito comment也提出了各种优秀的观点。所以如果字符串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)}
在这里,我们添加了一个更正以修复最后一条记录

如果有两条以上的AWK记录,则第一条记录始终为空,您需要从pRT中删除第一个新行字符,否则您将包含由最后一条记录引起的额外新行,该记录以新行结尾,与所有其他记录不同。 如果文本中只有两条AWK记录,一条有效,则不应进行此更正,因为第一条RT不会以新行开始 最终的改进是通过认识到我们总是删除pRT中的初始换行符(如果有),因此我们可以将其合并到一个gsub中:

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

RS成为正则表达式的能力是一个笨拙的扩展。在大多数其他AWK实现中,或者如果gawk处于兼容模式,请参见选项,只使用RS值的第一个字符

ORS:输出记录分隔符。它在每个打印语句的末尾输出。它的默认值是\n换行符

RT:gnuawk指定与记录分隔符RS表示的文本相匹配的输入文本。每次读取记录时都会设置它

资料来源:


在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,该变量可以被读取为找到的上一个记录分隔符

正如中所指出的,您仍然需要更新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=^ |\na[0-9]+[.]Dit o评论也提出了各种极好的观点。所以如果字符串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)}
在这里,我们添加了一个更正以修复最后一条记录

如果有两条以上的AWK记录,则第一条记录始终为空,您需要从pRT中删除第一个新行字符,否则您将包含由最后一条记录引起的额外新行,该记录以新行结尾,与所有其他记录不同。 如果文本中只有两条AWK记录,一条有效,则不应进行此更正,因为第一条RT不会以新行开始 最终的改进是通过认识到我们总是删除pRT中的初始换行符(如果有),因此我们可以将其合并到一个gsub中:

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

RS成为正则表达式的能力是一个笨拙的扩展。在大多数其他AWK实现中,或者如果gawk处于兼容模式,请参见选项,只使用RS值的第一个字符

ORS:输出记录分隔符。它在每个打印语句的末尾输出。它的默认值是\n换行符

RT:gnuawk指定与记录分隔符RS表示的文本相匹配的输入文本。每次读取记录时都会设置它

资料来源:


这可能适用于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
把开始一段对话的行集合起来。其中n是一个整数。如果行包含单词foo,则进行所需替换并打印结果,否则不执行任何操作

道歉:当我开始解决这个问题时,问题被标记为sed


当一行开始一个字符时。遇到时,此行将替换保留空间中的内容。但是,在此之前,先检查保留空间,如果它包含单词foo,即集合已存在,则满足要处理的要求,因此将按要求格式化行并打印。其他行追加到保留空间。遇到文件结尾时会满足一个特殊条件,该条件与行开始时的条件相同。这可以通过添加goto标签来实现:a

这可能适用于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
把开始一段对话的行集合起来。其中n是一个整数。如果行包含单词foo,则进行所需替换并打印结果,否则不执行任何操作

道歉:当我开始解决这个问题时,问题被标记为sed


当一行开始一个字符时。遇到时,此行将替换保留空间中的内容。但是,在此之前,先检查保留空间,如果它包含单词foo,即集合已存在,则满足要处理的要求,因此将按要求格式化行并打印。其他行追加到保留空间。遇到文件结尾时会满足一个特殊条件,该条件与行开始时的条件相同。这可以通过添加goto标签来实现:a

对于GNU awk,您已经在使用它来处理多字符RS,包含与RS regexp匹配的字符串的内置变量是RT

我们需要修复您的RS设置,因为您需要一个用于RS的regexp,该regexp与行^ |\na[0-9]+[.]开头的a匹配,或者与文件结尾的新行匹配\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) }
以下是它所做的,考虑到这一输入,它包含了比问题中更多的未雨绸缪的情况,您应该针对这一点测试其他建议的解决方案:

输入:

输出:


对于已经用于多字符RS的GNU awk,包含与RS regexp匹配的字符串的内置变量是RT

我们需要修复您的RS设置,因为您需要一个用于RS的regexp,该regexp与行^ |\na[0-9]+[.]开头的a匹配,或者与文件结尾的新行匹配\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) }
以下是它所做的,考虑到这一输入,它包含了比问题中更多的未雨绸缪的情况,您应该针对这一点测试其他建议的解决方案:

输入:

输出:


你能解释一下为什么输出中没有a2值吗?@RavinderSing
h13在a2I之后的记录中没有字符串foo。为了清楚起见,我更新了您的问题。如果更新不符合您的期望,请随时回复。请您详细说明为什么输出中没有a2值?@RavinderSingh13以下记录中没有字符串foo。为了清楚起见,a2I已更新了您的问题。如果更新不符合您的期望,请随时恢复。很好。删除[0-9]打印awk'开始{RS=a[0-9]*;ORS=\n--\n}/foo/{print$0 ORT}{ORT=RT;sub.,ORT}'结尾处的点file@shaikisiegal我为您的解决方案添加了一个小更新!美元以下,,这是一个正则表达式。谢谢你指出这一点。很好。删除[0-9]打印awk'开始{RS=a[0-9]*;ORS=\n--\n}/foo/{print$0 ORT}{ORT=RT;sub.,ORT}'结尾处的点file@shaikisiegal我为您的解决方案添加了一个小更新!美元以下,,这是一个正则表达式。谢谢你指出这一点。我删除了大部分标签,因为它们对问题无关紧要。尽管如此,这条线还是很棒。如果你能解释一下你用保持空间和图案空间所做的魔术,那就太好了。出于教育目的,我删除了大部分标签,因为它们对问题无关紧要。尽管如此,这条线还是很棒。如果你能解释一下你用保持空间和图案空间所做的魔术,那就太好了。这是出于教育目的。
$ 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
-----