# file: 'test.txt'
foobar bash 1
bash
foobar happy
foobar
我只想知道在“ foobar”之后出现的单词,所以我可以使用此正则表达式:
"foobar \(\w\+\)"
括号表明,我对foobar之后的单词有特殊的兴趣。但是,当我执行
grep "foobar \(\w\+\)" test.txt
时,我得到的是与整个正则表达式匹配的整行,而不仅仅是“ foobar之后的单词”:该命令的输出如下所示:foobar bash 1
foobar happy
是否有办法告诉grep只输出与分组中的分组(或特定分组)匹配的项目?正则表达式?
#1 楼
GNU grep对于perl样式的正则表达式具有-P
选项,而-o
选项仅用于打印与模式匹配的内容。可以使用环顾断言(在perlre联机帮助页中的扩展模式下进行描述)将它们组合在一起,以从确定为-o
的目的已匹配的内容中删除部分grep模式。 /> \K
是(?<=pattern)
的缩写形式(更有效的形式),您可以在要输出的文本之前将其用作零宽度后向断言。 (?=pattern)
可以用作要输出的文本后的零宽度超前断言。例如,如果要在
foo
和bar
之间匹配单词,则可以使用:$ grep -oP 'foobar \K\w+' test.txt
bash
happy
$
或(对称)
$ grep -oP 'foo \K\w+(?= bar)' test.txt
评论
如果您的正则表达式包含多个分组,您该怎么办? (如标题所示?)
– barracel
13年3月21日在7:52
@barracel:我不相信你可以。 sed时间(1)
– camh
13年3月22日在22:51
@camh我刚刚测试了grep -oP'foobar \ K \ w +'test.txt与OP的test.txt没有任何输出。 grep版本是2.5.1。有什么问题吗? O_O
– SOUser
2014年7月24日14:19在
@XichenLi:我不能说。我刚刚建立了grep v2.5.1(它很老-从2006年开始),它对我有用。
– camh
2014年7月25日上午10:18
似乎-P标志至少在Mac El Capitan上不起作用
– OZZIE
18年1月25日在10:39
#2 楼
sed -n "s/^.*foobar\s*\(\S*\).*$//p"
-n suppress printing
s substitute
^.* anything before foobar
foobar initial search match
\s* any white space character (space)
\( start capture group
\S* capture any non-white space character (word)
\) end capture group
.*$ anything after the capture group
substitute everything with the 1st capture group
p print it
评论
sed示例的+1似乎比grep更好。一个评论,^和$是多余的,因为。*是贪婪的匹配。但是,包括它们可能有助于阐明正则表达式的意图。
–托尼
18年5月30日在21:22
对我来说,开始时以。*为首。否则,它还会捕获foobar之前的内容。
–aerijman
2月19日18:37
由于某种原因,这似乎不适用于macOS sed:echo“ foobar bash 1” | sed -n“ s /^.* foobar \ s * \(\ S * \)。* $ / \ 1 / p”不输出任何内容。
– Frederik
11月27日15:57
#3 楼
标准grep无法做到这一点,但是GNU grep的最新版本可以做到。您可以转向sed,awk或perl。这里有一些示例,您可以根据自己的示例输入进行操作。它们在极端情况下的行为略有不同。如果是foobar word other stuff
,请打印第二个单词。sed -n -e 's/^foobar \([[:alnum:]]\+\).*//p'
如果是第一个单词,请粘贴
word
,否则跳过该行;然后在第一个空格之后剥离所有内容并打印。awk ' == "foobar" {print }'
评论
太棒了!我以为我可以使用sed来做到这一点,但是我以前从未使用过它,而是希望可以使用熟悉的grep。但是,由于我熟悉vim样式的搜索和替换+正则表达式,因此这些命令的语法实际上看起来非常熟悉。万分感谢。
– Cory Klein
2011年5月19日23:51
不对,吉尔斯。请参阅我的答案以获取GNU grep解决方案。
– camh
2011年5月20日下午1:33
@camh:啊,我不知道GNU grep现在有完整的PCRE支持。我已经纠正了我的回答,谢谢。
–吉尔斯'所以-不再是邪恶的'
2011年5月20日7:14
由于Busybox grep不支持PCRE,因此该答案对于嵌入式Linux特别有用。
–克雷格·麦昆(Craig McQueen)
16 Mar 17 '16 at 0:12
显然,有多种方法可以完成所介绍的同一任务,但是,如果OP要求使用grep,为什么还要回答其他问题呢?另外,您的第一段是不正确的:是的,grep可以做到。
–fcm
19年3月11日在13:31
#4 楼
好吧,如果您知道foobar始终是第一个单词或行,那么可以使用cut。像这样:grep "foobar" test.file | cut -d" " -f2
评论
grep上的-o开关得到了广泛的实现(比Gnu grep扩展还要多),因此使用grep -o“ foobar” test.file | cut -d“” -f2将提高此解决方案的有效性,比使用后向断言更可移植。
– dubiousjim
2012年4月19日在21:04
我相信您将需要grep -o“ foobar。*”或grep -o“ foobar \ w +”。
– G-Man说“恢复莫妮卡”
18年4月14日在7:20
如果值中还有另一个空格则中断
–mvmn
19/12/27在15:17
#5 楼
pcregrep
具有更智能的-o
选项,使您可以选择要输出的捕获组。
因此,使用您的示例文件,
$ pcregrep -o1 "foobar (\w+)" test.txt
bash
happy
评论
哇,这对我来说很神奇,非常感谢。我在MacOS上,并尝试以某种方式使用匹配组。我一直尝试zegrep的原因是因为我压缩了一个大的zip文件,但还发现pcregrep将(从pcregrep --help页面):使用zlib读取名称以.gz结尾的文件。因此,我可以立即在zip文件中使用它。再次感谢!
– Samjewell
4月6日15:11
#6 楼
如果不支持PCRE,则可以通过两次调用grep获得相同的结果。例如,要在foobar之后抓单词,请执行以下操作: ><test.txt grep -o 'foobar *[^ ]*' | grep -o '[^ ]*$'
输出:
i=1
<test.txt egrep -o 'foobar +([^ ]+ +){'$i'}[^ ]+' | grep -o '[^ ]*$'
请注意,索引
i
是从零开始的。#7 楼
使用grep
不跨平台兼容,因为-P
/ --perl-regexp
仅在GNU grep
上可用,而不在BSD grep
上可用。这里是使用
ripgrep
的解决方案:$ rg -o "foobar (\w+)" -r '' <test.txt
bash
happy
man rg
:-r
/ --replace REPLACEMENT_TEXT
用给定的文本替换每个匹配项。替换字符串支持捕获组索引(例如
)和名称(例如$foo
)。 > 相关:GH-462。
评论
但是可以在BSD发行版上安装gnugrep。
–bparker
5月13日20:50
#8 楼
我发现@jgshawkey的答案非常有帮助。grep
并不是一个很好的工具,但sed是,尽管这里有一个使用grep抓取相关行的示例。 如果您不习惯sed的正则表达式语法,则它是特殊的。
这是另一个示例:该示例解析xinput的输出以获得ID整数
⎜ ↳ SynPS/2 Synaptics TouchPad id=19 [slave pointer (2)]
我想要19
export TouchPadID=$(xinput | grep 'TouchPad' | sed -n "s/^.*id=\([[:digit:]]\+\).*$//p")
请注意类语法:
[[:digit:]]
并需要转义以下
+
我假设只有一行匹配。
评论
这正是我试图做的。谢谢!
–詹姆斯
19年5月12日在0:07
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | sed -nE“ s /.* TouchPad。+ id =([0-9] +)。* / \ 1 / p”
–阿米特·奈杜(Amit Naidu)
19年5月19日在5:10
评论
对于不需要grep的用户:perl -lne'如果/ foobar(\ w +)/则打印$ 1'为什么需要转义括号和+?
表示正则表达式分组并匹配1个或多个字符,而不是搜索实际的字符(,)和+。