AWK:从线条图案访问捕获的组


229

如果我有awk命令

pattern { ... }

和模式使用捕获组,如何访问在块中捕获的字符串?



有时(在简单情况下)可以调整字段分隔符(FS),然后选择要与匹配的内容$field。对输入进行预格式化也可能有帮助。
KrzysztofJabłoński'15

1
在重复问题上有一个更好的答案
塞缪尔·埃德温·沃德

2
塞缪尔·埃德温·沃德(Samuel Edwin Ward):这也是一个不错的答案!但它也需要gawk(因为使用gensub)。
猖ion

Answers:


176

那是一段记忆的小路...

很久以前,我用perl代替了awk。

显然,AWK正则表达式引擎无法捕获其组。

您可以考虑使用类似:

perl -n -e'/test(\d+)/ && print $1'

-n标志使perl像awk一样遍历每一行。


3
显然有人不同意。该网页来自2005年:tek-tips.com/faqs.cfm?fid=5674 它确认您无法在awk中重用匹配的组。
Peter Tillemans 2010年

3
在几乎所有用例中,我都更喜欢'perl -n -p -e ...',而不是awk,因为我认为它更灵活,更强大并且语法更简洁。
Peter Tillemans

15
gawk!= awk。它们是不同的工具,gawk在大多数地方默认情况下不可用。
奥利(Oli)2012年

6
OP特别要求awk解决方案,所以我认为这不是答案。
2013年

6
@Joppe如果没有解决方案,您将无法提供awk解决方案。在第3行中,我解释说AWK不支持捕获组,因此我给出了一个替代方案,OP对此表示赞赏,因为此答案被接受。我怎样才能更好地回答这个问题?
彼得·提勒曼斯

335

使用gawk,可以使用该match功能捕获带括号的组。

gawk 'match($0, pattern, ary) {print ary[1]}' 

例:

echo "abcdef" | gawk 'match($0, /b(.*)e/, a) {print a[1]}' 

输出cd

请注意实现相关功能的gawk的具体用法。

对于便携式替代品,您可以使用match()和实现类似的结果substr

例:

echo "abcdef" | awk 'match($0, /b[^e]*/) {print substr($0, RSTART+1, RLENGTH-1)}'

输出cd


4
是的,gxxx变体还有很多其他的GNU优点和功能。
Peter Tillemans

在BusyBox awk中也可以使用。
MrMas

32

这是我一直需要的东西,因此我为其创建了一个bash函数。它基于格伦·杰克曼的答案。

定义

将此添加到您的.bash_profile等。

function regex { gawk 'match($0,/'$1'/, ary) {print ary['${2:-'0'}']}'; }

用法

为文件中的每一行捕获正则表达式

$ cat filename | regex '.*'

为文件中的每一行捕获第一个正则表达式捕获组

$ cat filename | regex '(.*)' 1

2
与使用有什么不同grep -o
bfontaine

@bfontaine可以grep -o输出捕获的组吗?
OlleHärstedt'3

1
@OlleHärstedt不行。仅当您没有捕获组时,它才涵盖您的用例。在这种情况下,链式grep -o的使用会很丑陋。
bfontaine

15

您可以使用GNU awk:

$ cat hta
RewriteCond %{HTTP_HOST} !^www\.mysite\.net$
RewriteRule (.*) http://www.mysite.net/$1 [R=301,L]

$ gawk 'match($0, /.*(http.*?)\$/, m) { print m[1]; }' < hta
http://www.mysite.net/

12
+1。另外,无论如何:awk 'match($0, /.*(http.*?)\$/) { print substr($0,RSTART,RLENGTH) }'
Ed Morton 2012年


1
埃德·莫顿(Ed Morton):我应该说一个顶级的答案。编辑:嗯... RewriteRule (.*) http://www.mysite.net/$为我打印,比子组还多。
风铃草


4

您也可以在香草awk中模拟捕获,而无需扩展。它不是很直观:

步骤1.使用gensub将匹配项与字符串中未出现的某些字符包围起来。步骤2.对字符使用split。步骤3.拆分数组中的所有其他元素都是您的捕获组。

$ echo'ab cb ad'| awk'{split(gensub(/ a ./,SUBSEP“&” SUBSEP,“ g”,$ 0),cap,SUBSEP); 打印帽[2]“ |” 上限[4];}'
Ab | ad

3
我几乎可以肯定这gensub是一个gawk特定的功能。如果输入awk --version;-?,您会从awk中得到什么。祝你们好运。
Shellter

6
我完全确定gensub是一个怪诞主义,尽管BusyBox awk也有它。不过,也可以使用gsub来实现此答案:echo 'ab cb ad' | awk '{gsub(/a./,SUBSEP"&"SUBSEP);split($0,cap,SUBSEP);print cap[2]"|"cap[4]}'
dubiousjim 2012年

3
gensub()是gawk的扩展名,gawk的手册明确指出了这一点。其他awk变体也可以实现它,但是它仍然不是POSIX。试试gawk --posix'{gsub(...)}',它会抱怨
MestreLion'4

2
@MestreLion,您的意思是它将抱怨gawk --posix '{gensub(...)}'
dubiousjim 2012年

1
尽管您对具有该功能的POSIX awk错误gensub,但您的示例仅适用于非常有限的情况:整个模式已分组,key=(value)当我只想提取value部分时,它无法匹配所有模式。
2015年

2

我想出一个包装了Peter Tillemans答案的bash函数有些费劲,但这是我想到的:

函数regex {perl -n -e“ / $ 1 / && printf \”%s \ n \“,”'$ 1'}

对于以下正则表达式参数,我发现此方法比opsb的基于awk的bash函数更好,因为我不希望打印“ ms”。

'([0-9]*)ms$'

我更喜欢这种解决方案,因为您可以看到组中限制捕获的部分,同时也忽略了它们。但是,有人能做到这一点吗?我无法在Perl BASH中正常使用此perl语法,因为我不太了解它-特别是左右的双引号$1
-Demis

这不是我之前或之后所做的事情,但是回想一下它正在做什么,请串联两个字符串,第一个字符串用双引号引起来(此第一个字符串包含用反斜杠转义的嵌入式双引号),第二个字符串用单引号引起来。然后,将该连接的结果作为参数提供给perl -e。您还需要知道用函数的第一个参数替换第一个$ 1(双引号中的一个),而保持第二个$ 1(单引号中的一个)不变。看到这个例子
wytten

我知道,这现在变得更有意义了。那么,在perl命令中,正则表达式匹配/组捕获定义在哪里?我看到你写的'([0-9]*)ms$'-是作为参数(字符串是另一个参数)提供的吗?然后将来自的输出perl -e插入到bash的printf命令中,以进行替换%s,对吗?谢谢,我希望使用这个。
Demis

1
您将用单引号引起来的正则表达式作为正则表达式bash函数的唯一参数传递。示例
wytten
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.