如何使用 awk 打印匹配的正则表达式模式?
Posted
技术标签:
【中文标题】如何使用 awk 打印匹配的正则表达式模式?【英文标题】:How to print matched regex pattern using awk? 【发布时间】:2011-07-29 00:08:20 【问题描述】:使用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
这正是我所需要的 :) 非常感谢!
【问题讨论】:
@maxtaldykin 您能否将问题中的自我回答移到单独的答案中? 你不需要做tmp=match($i, /regexp);if(tmp)
,你应该可以做if(tmp ~ $i)
,因为~
的意思是“匹配正则表达式”。
【参考方案1】:
echo "abc123def" | awk '
function MATCH(haystack, needle, ltrim, rtrim)
if(ltrim == 0 && !length(ltrim))
ltrim = 0;
if(rtrim == 0 && !length(rtrim))
rtrim = 0;
return substr(haystack, match(haystack, needle) + ltrim, RLENGTH - ltrim - rtrim);
print $0 " - " MATCH($0, "123"); # 123
print $0 " - " MATCH($0, "[0-9]*d", 0, 1); # 123
print $0 " - " MATCH($0, "1234"); # Nothing printed
'
【讨论】:
【参考方案2】:听起来您正在尝试模仿 GNU 的 grep -o
行为。这将做到这一点,前提是您只需要每行的第一个匹配项:
awk 'match($0, /regex/)
print substr($0, RSTART, RLENGTH)
' file
这是一个示例,使用 GNU 的 awk
实现 (gawk):
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
在awk
手册中了解match
、substr
、RSTART
和RLENGTH
。
之后,您可能希望扩展它以处理同一行上的多个匹配项。
【讨论】:
注:要回答最后一部分,所有需要的结构都在 kurumi's answer 和我自己的。 很好的答案。只是我想在这里解释一下,因为我很懒。但这就是我使用 AWK 的原因! 如果我想对匹配结果做一些事情,除了打印它?例如,我想将所有匹配项添加到数组中。 @evya2005:您可以简单地将呼叫 Ron print 替换为您需要的作业。 它对我不起作用。仅打印工作。你能给我举个例子吗?【参考方案3】:如果您知道要查找的文本/模式(例如“yyy”)在哪一列,您只需检查该特定列以查看它是否匹配,然后打印出来。
例如,给定一个包含以下内容的文件,(称为 asdf.txt)
xxx yyy zzz
如果第二列与模式“yyy”匹配,则仅打印第二列,您可以执行以下操作:
awk '$2 ~ /yyy/ print $2' asdf.txt
请注意,这也将基本上匹配第二列中包含“yyy”的任何行,如下所示:
xxx yyyz zzz
xxx zyyyz
【讨论】:
【参考方案4】:题外话,这也可以使用 grep 来完成,如果有人正在寻找 grep 解决方案,只需在此处发布即可
echo 'xxx yyy zzze ' | grep -oE 'yyy'
【讨论】:
即使使用正则表达式也能轻松获取它。正是我需要的。谢谢! 这对我有用;我的情况是这样的: echo "web_port=8080,shutdown_port=8005" | grep -oE "web_port=[0-9]+" # 返回 8080【参考方案5】:如果您只对输入的最后一行感兴趣并且只希望找到一个匹配项(例如 shell 命令的摘要行的一部分),您也可以试试这个非常紧凑的代码,采用自 @987654321 @:
$ echo "xxx yyy zzz" | awk 'match($0,"yyy",a)ENDprint a[0]'
yyy
或者更复杂的部分结果:
$ echo "xxx=a yyy=b zzz=c" | awk 'match($0,"yyy=([^ ]+)",a)ENDprint a[1]'
b
警告:带三个参数的awk
match()
函数只存在于gawk
中,而不存在于mawk
中
这是另一个很好的解决方案,在grep
中使用lookbehind regex 而不是awk
。此解决方案对您的安装要求较低:
$ echo "xxx=a yyy=b zzz=c" | grep -Po '(?<=yyy=)[^ ]+'
b
【讨论】:
为什么要加“tail -n1”?没有它应该可以正常工作,不是吗? @ArthurAccioly 正确。我使用该术语从 ping 调用中提取平均往返时间,这就是它的来源。有趣的是花了 4 年才发现它;)【参考方案6】:在这种情况下使用 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
@GrigoryEntin - bsd sed 适用于原始答案。 POSIX 支持的扩展正则表达式开关是 -E,但在 FreeBSD 中,至少 -r 与 -E 相同(2010 年添加了 -r)。无论如何,尝试使用 -E(gnu sed 在 4.3 中添加了 -E)【参考方案7】:
如果 Perl 是一个选项,你可以试试这个:
perl -lne 'print $1 if /(regex)/' file
要实现不区分大小写的匹配,请添加i
修饰符
perl -lne 'print $1 if /(regex)/i' file
在比赛后打印所有内容:
perl -lne 'if ($found)print elseif (/regex(.*)/)print $1; $found++' textfile
打印比赛和比赛后的一切:
perl -lne 'if ($found)print elseif (/(regex.*)/)print $1; $found++' textfile
【讨论】:
这实际上完全符合我的要求。无法使用 awk 打印我的匹配项,但 perl 成功了。【参考方案8】:gawk 可以使用此作为操作来获取每一行的匹配部分:
if (match($0,/your regexp/,m)) print m[0]
匹配(字符串,正则表达式 [,数组]) 如果数组存在,则将其清除, 然后将数组的第零个元素设置为的整个部分 正则表达式匹配的字符串。如果 regexp 包含括号,则 数组的整数索引元素设置为包含 字符串匹配相应的括号子表达式。 http://www.gnu.org/software/gawk/manual/gawk.html#String-Functions
【讨论】:
【参考方案9】:这是最基本的
awk '/pattern/ print $0 ' file
让awk
使用//
搜索pattern
,然后打印出该行,默认情况下称为记录,用$0 表示。至少阅读documentation。
如果您只想打印出匹配的单词。
awk 'for(i=1;i<=NF;i++) if($i=="yyy")print $i ' file
【讨论】:
因为print
是默认操作:awk '/pattern/' file
就足够了。
@Johnsyweb,是的,我知道这个事实。对于像 marverix 这样的初学者来说,它意味着更直观。
我不怀疑你的知识。但是,这些信息可能对其他找到此答案的人有用。
注意:如果 (a) "yyy" 是一个正则表达式而不是一个直字符串,并且 (b) 如果“yyy”与记录中的整个字段不匹配。
不会是$i=="yyy"
;对于正则表达式,它将是 $i ~ /yyy/
。以上是关于如何使用 awk 打印匹配的正则表达式模式?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 awk 命令在匹配正则表达式之前和之后打印 5 行