如何使用awk打印匹配的正则表达式模式?


109

使用awk,我需要在文件中找到与正则表达式模式匹配的单词。

只是想打印与模式匹配的单词。

因此,如果在这一行中,我有:

xxx yyy zzz

和模式:

/yyy/

我只想得到:

yyy

编辑:感谢kurumi,我设法写了这样的东西:

awk '{
        for(i=1; i<=NF; i++) {
                tmp=match($i, /[0-9]..?.?[^A-Za-z0-9]/)
                if(tmp) {
                        print $i
                }
        }
}' $1

这就是我所需要的:)非常感谢!


1
@maxtaldykin您能否将自己的答案从问题中移到单独的答案中?
kenorb

2
您不需要做tmp=match($i, /regexp);if(tmp){},您应该可以做,if(tmp ~ $i){}因为它的~意思是“匹配正则表达式”。
JustinCB

Answers:


148

这是非常基本的

awk '/pattern/{ print $0 }' file

要求awk搜索patternusing //,然后打印出该行,默认情况下称为记录,用$ 0表示。至少阅读文档

如果只想打印出匹配的单词。

awk '{for(i=1;i<=NF;i++){ if($i=="yyy"){print $i} } }' file

49
因为print是默认操作:awk '/pattern/' file将足够。
Johnsyweb 2011年

18
@Johnsyweb,是的,我确实知道这个事实。对于像marverix这样的初学者,它的意思是更具视觉效果。
kurumi 2011年

21
我毫不怀疑你的知识。但是,该信息可能对其他找到此答案的人有用。
Johnsyweb 2011年

2
注意:for如果(a)“ yyy”是一个正则表达式而不是一个直串,并且(b)如果“ yyy”不匹配其中的整个字段,则@marverix将需要更多的功课才能使-loop起作用。一个记录。
Johnsyweb 2011年

8
不会的$i=="yyy"; 这将是$i ~ /yyy/一个正则表达式。
JustinCB

118

听起来您正在尝试模仿GNU的grep -o行为。如果您只希望每行的第一个匹配项,则会这样做:

awk 'match($0, /regex/) {
    print substr($0, RSTART, RLENGTH)
}
' file

这是一个使用GNU awk实现的示例():

awk 'match($0, /a.t/) {
    print substr($0, RSTART, RLENGTH)
}
' /usr/share/dict/words | head
act
act
act
act
aft
ant
apt
art
art
art

阅读matchsubstrRSTARTRLENGTHawk手册。

之后,您可能希望扩展它以处理同一行上的多个匹配项。


注意:要回答最后一部分,所有需要的构造都在 kurumi的回答中和我自己的回答中
Johnsyweb 2011年

好答案。我只是想在这里解释一下,因为我很懒。但这就是为什么我使用AWK!
lukas.pukenis

如果我想对匹配结果进行除打印外的处理?例如,我想将所有匹配项添加到数组中。
Evya2005 '17

@ evya2005:您可以简单地将呼叫Ron打印替换为所需的任务。
Johnsyweb

它对我不起作用。仅打印作品。你能给我看看例子吗?
Evya2005 '17

36

使用此操作, gawk可以获取每一行的匹配部分:

{ if (match($0,/your regexp/,m)) print m[0] }

match(string,regexp [,array])如果存在array,则将其清除,然后将array的第零个元素设置为与regexp匹配的字符串的整个部分。如果regexp包含括号,则将array的整数索引元素设置为包含与相应括号化的子表达式匹配的字符串部分。 http://www.gnu.org/software/gawk/manual/gawk.html#String-Functions


13

如果您只对输入的最后一行感兴趣,并且希望只找到一个匹配项(例如,shell命令的摘要行的一部分),则还可以尝试一下这种非常紧凑的代码,该代码摘自How to print regexp matchs使用`awk`?

$ echo "xxx yyy zzz" | awk '{match($0,"yyy",a)}END{print a[0]}'
yyy

或更复杂的版本有部分结果:

$ echo "xxx=a yyy=b zzz=c" | awk '{match($0,"yyy=([^ ]+)",a)}END{print a[1]}'
b

警告:awk match()具有三个参数的函数仅存在于中gawk,不存在于mawk

下面是一个使用另一种很好的解决方案回顾后发正则表达式grep代替awk。该解决方案对您的安装有较低的要求:

$ echo "xxx=a yyy=b zzz=c" | grep -Po '(?<=yyy=)[^ ]+'
b

为什么要添加“ tail -n1”?没有它就可以正常工作,不是吗?
亚瑟·阿乔利

1
@ArthurAccioly正确。我用这个术语从ping呼叫中提取平均往返时间,而这正是它的来源。有趣的是花了4年才发现它;)
Daniel Alder

12

如果可以选择Perl,则可以尝试以下操作:

perl -lne 'print $1 if /(regex)/' file

要实现不区分大小写的匹配,请添加i修饰符

perl -lne 'print $1 if /(regex)/i' file

在比赛后打印所有内容:

perl -lne 'if ($found){print} else{if (/regex(.*)/){print $1; $found++}}' textfile

要打印比赛以及比赛后的所有内容:

perl -lne 'if ($found){print} else{if (/(regex.*)/){print $1; $found++}}' textfile

3

在这种情况下,使用sed也可以很优雅。示例(用行中的匹配组“ yyy”替换行):

$ cat testfile
xxx yyy zzz
yyy xxx zzz
$ cat testfile | sed -r 's#^.*(yyy).*$#\1#g'
yyy
yyy

相关手册页:https : //www.gnu.org/software/sed/manual/sed.html#Back_002dreferences-and-Subexpressions


对于不使用gnu sed的解决方案是这样的:sed -n 's/^.*\(yyy\).*$/\1/gp' < testfile
Grigory Entin '18

1
@GrigoryEntin-bsd sed可以很好地处理原始答案。POSIX支持的扩展正则表达式开关是-E,但在FreeBSD中,至少-r与-E相同(在2010年添加了-r)。无论如何,尝试用-E(GNU的sed在4.3加入-E)
胡安

3

离题,这也可以使用grep来完成,只要有人在寻找grep解决方案,就可以在此处发布

echo 'xxx yyy zzze ' | grep -oE 'yyy'

即使使用正则表达式也可以轻松获取它。正是我所需要的。谢谢!
Marquee

这对我有用;我的情况是:echo“ web_port = 8080,shutdown_port = 8005” | grep -oE“ web_port = [0-9] +”#return 8080
曾荫权

0

如果您知道要查找的文本/图案(例如“ yyy”)位于哪一列,则只需检查该特定列以查看其是否匹配,然后进行打印即可。

例如,给定一个具有以下内容的文件(称为asdf.txt

xxx yyy zzz

仅在第二列与模式“ yyy”匹配时才打印,您可以执行以下操作:

awk '$2 ~ /yyy/ {print $2}' asdf.txt

请注意,这基本上也将匹配第二列中包含“ yyy”的任何行,如下所示:

xxx yyyz zzz
xxx zyyyz
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.