说我有一个文件。
# 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
我更希望该命令的输出是这样的。
bash
happy
有没有办法告诉grep只输出符合正则表达式中的分组(或特定分组)的项目?
GNU grep有-P
选项用于perl-style regexes,还有-o
选项用于只打印符合模式的内容。这两个选项可以通过查找断言(在perlre manpage的Extended Patterns中描述)结合起来,将grep模式中的部分内容从-o
的目的中确定为匹配的内容。
$ grep -oP 'foobar \K\w+' test.txt
bash
happy
$
K
是(?<=pattern)
的简写形式(也是更有效的形式),在你想输出的文本之前,你可以用它作为零宽度的look-behind断言。(?=pattern)
可以作为你想输出的文本之后的零宽度look-ahead断言。
例如,如果你想匹配foo
和bar
之间的词,你可以使用。
$ grep -oP 'foo \K\w+(?= bar)' test.txt
或(为了对称性)
$ grep -oP '(?<=foo )\w+(?= bar)' test.txt
标准grep不能这样做,但GNU grep的最新版本可以。你可以求助于sed、awk或perl。这里有几个例子,在你的输入样本上做你想做的事;它们在角落里的表现略有不同。
用word
替换foobar word other stuff
,只在替换完成后打印。
sed -n -e 's/^foobar \([[:alnum:]]\+\).*/\1/p'
如果第一个词是foobar
,则打印第二个词。
awk '$1 == "foobar" {print $2}'
如果 "foobar "是第一个词,则剥离它,否则跳过该行;然后剥离第一个空格后的所有内容并打印。
perl -lne 's/^foobar\s+// or next; s/\s.*//; print'
sed -n "s/^.*foobar\s*\(\S*\).*$/\1/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
\1 substitute everything with the 1st capture group
p print it
使用grep
是不跨平台的,因为-P
/--perl-regexp
只能在[GNU grep
][1]上使用,而不是[BSD grep
][2]。
下面是使用 [ripgrep
][3] 的解决方案。
$ rg -o "foobar (\w+)" -r '$1' <test.txt
bash
happy
按照 "man rg"。
-r
/--replace REPLACEMENT_TEXT
用给定的文本替换每个匹配项。
替换字符串中支持捕获组索引(如
$5
)和名称(如$foo
)。
相关。 [GH-462][4].
[1]: https://www.gnu.org/software/grep/manual/grep.html [2]: https://man.openbsd.org/grep [3]: https://github.com/BurntSushi/ripgrep [4]: https://github.com/rust-lang/regex/issues/462
我发现@jgshawkey的回答非常有用。
grep
不是一个很好的工具,但是sed是,虽然这里我们有一个使用grep来抓取相关行的例子。
sed的Regex语法是特异性的,如果你不习惯它。
这里还有一个例子。 这个例子是解析xinput的输出,得到一个ID整数
⎜ ↳ SynPS/2 Synaptics TouchPad id=19 [slave pointer (2)]
而我要19
export TouchPadID=$(xinput | grep 'TouchPad' | sed -n "s/^.*id=\([[:digit:]]\+\).*$/\1/p")
注意类的语法。
[[:digit:]]
以及需要避开以下+
的内容。
我假设只有一条线匹配。