![如何在 awk 正则表达式中引用正则表达式组?](https://linux22.com/image/159352/%E5%A6%82%E4%BD%95%E5%9C%A8%20awk%20%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E4%B8%AD%E5%BC%95%E7%94%A8%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F%E7%BB%84%EF%BC%9F.png)
如何在 awk 正则表达式中引用正则表达式组?例如,如果我有一个正则表达式组(\w)
,我以后如何在同一个正则表达式中引用它,例如(\w)\1
? awk 支持这个功能吗?下面的例子不起作用。
# In this example, I want to change aa to aaa and cc to ccc.
echo ab aa cc de mn | gawk '{print gensub(/(\w)\1/, "\\1\\1\\1", "g")}'
# The result is: ab aa cc de mn
# The expected result is: ab aaa ccc de mn
答案1
busybox 的实现awk
是我所知道的唯一支持反向引用的实现。它确实也支持gawk
'sgensub()
和\w
扩展:
与sub()
and一样gsub()
,您必须使用"..."
代替/.../
和使用\\1
代替\1
(在标准中awk
,是值 1 ( )"\1"
的字符,并且需要与该字符匹配,而is (好吧^A
/\1/
"\\1"
曾是) POSIX 中未指定;另请注意,POSIX ERE 没有反向引用,这是 BRE 具有但 ERE 没有的一项功能)。
$ echo ab aa cc de mn | busybox awk '{print gensub("(\\w)\\1", "\\1\\1\\1", "g")}'
ab aaa ccc de mn
请注意,虽然 busyboxawk
不是国际化,它只\w
匹配,a-zA-Z0-9_
无论区域设置如何(与 相同[[:alnum:]]
)并且不支持多字节字符:
$ echo ee éé | busybox awk '{print gensub("(\\w)\\1", "\\1\\1\\1", "g")}'
eee éé
对于标准实用程序,您通常会用于sed
该工作:
sed 's/\([[:alnum:]_]\)\1/&\1/g'
sed
正则表达式是基本的支持反向引用的正则表达式。一些sed
实现支持扩展带有-r
or 的正则表达式-E
,POSIX 将-E
在标准的下一个主要版本中指定,但仍然不返回引用(尽管捕获组 fors
的替换将指定)。 GNU 和 busyboxsed
确实支持反向引用,-E
但 FreeBSDsed
不支持。
答案2
$ echo ab aa cc de mn | perl -pe 's/(\w)\1/\1\1\1/g'
ab aaa ccc de mn
有时您必须接受有些事情 awk 无法做到,但 Perl 可以。
好的一面是,如果您足够熟练地awk
使用gensub
并想要进行反向引用,那么您应该会发现perl
这是轻而易举的事。也就是说,如果你会写 awk,你就能写 perl。
答案3
awk
这可能超出了问题的范围,但是不支持反向引用的原因是因为awk
一直使用真实的正则表达式,即可以实现的表达式没有递归由有限状态机。这样的实现不能支持任何形式的反向引用(它可以支持捕获组,尽管实现并不简单)。
在awk
我看来,你应该使用正则表达式来进行直接的时间和内存限制的匹配,并使用类似于 C 的图灵完备语言来处理任何比这更复杂的事情。
相反,来自 perl / pcre / 等的“regexp”已经发展成为一种紧凑的语法来描述只能由图灵机实现的递归匹配过程。这具有安全隐患:不受信任的用户可以输入此类正则表达式的任何搜索框等都会导致拒绝服务攻击;没有人知道这样的比赛需要多少时间或内存,只能采取粗暴的措施,比如对其进行硬性任意限制和禁止持续性的猪。
这是一个旧文章作者 Russ Cox,其中对所有这些进行了更深入的描述。
答案4
不幸的是,在 POSIX 或任何其他非 busybox awk 中,需要对每行中的所有唯一字符进行循环,因为正则表达式中不支持反向引用:
$ cat tst.awk
{
old = new = $0
while (old != "") {
char = substr(old,1,1)
gsub(char,"",old)
if ( char ~ /[[:alnum:]_]/ ) {
gsub(char char,char char char,new)
}
}
print new
}
$ echo ab aa cc de mn | awk -f tst.awk
ab aaa ccc de mn
当目标字符不是正则表达式元字符(如本例所示)时,上述方法有效。如果它们可能是 RE 元字符,那么您需要在 gsub() 的正则表达式上下文中使用它们之前对它们进行转义。如果您愿意,可以在 gsub() 正则表达式中使用char"{2}"
而不是。char char
有关如何使用 busybox awk 执行此任务的信息,请参阅@Stephane 的答案。