我如何 grep 包含两个单词中的一个但不是两个单词的行?

Tra*_*mos 30 grep

我试图使用grep仅显示包含两个单词中的任何一个的行,如果其中一个出现在行中,但如果它们在同一行中则不显示。

到目前为止,我已经尝试过, grep pattern1 | grep pattern2 | ...但没有得到我预期的结果。

Chr*_*ris 61

其他工具grep是要走的路。

例如,使用 perl,命令将是:

perl -ne 'print if /pattern1/ xor /pattern2/'
Run Code Online (Sandbox Code Playgroud)

perl -ne在 stdin 的每一行上运行给定的命令,在这种情况下,如果它匹配/pattern1/ xor /pattern2/,则打印该行,或者换句话说,匹配一个模式但不匹配另一个(异或)。

这适用于任一顺序的模式,并且应该比多次调用 具有更好的性能grep,并且输入也更少。

或者,甚至更短,使用 awk:

awk 'xor(/pattern1/,/pattern2/)'
Run Code Online (Sandbox Code Playgroud)

或者对于没有的 awk 版本xor

awk '/pattern1/+/pattern2/==1`
Run Code Online (Sandbox Code Playgroud)

  • @吉姆。您可以在模式本身中放置单词边界(`\b`),即`\bword\b`。 (4认同)

Hax*_*iel 31

使用 GNU grep,您可以将两个词传递给grep然后删除包含这两个模式的行。

$ cat testfile.txt
abc
def
abc def
abc 123 def
1234
5678
1234 def abc
def abc

$ grep -w -e 'abc' -e 'def' testfile.txt | grep -v -e 'abc.*def' -e 'def.*abc'
abc
def
Run Code Online (Sandbox Code Playgroud)


Siv*_*iva 17

试试 egrep

egrep  'pattern1|pattern2' file | grep -v -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'
Run Code Online (Sandbox Code Playgroud)

  • 另外,请注意 grep 手册页:`不推荐直接调用 egrep 或 fgrep` -- 更喜欢 `grep -E` (8认同)
  • 也可以写成`grep -e foo -e bar | grep -v -e 'foo.*bar' -e 'bar.*foo'` (3认同)

Sté*_*las 12

使用grep支持类似 perl 的正则表达式(如pcregrep或 GNU 或 ast-open grep -P)的实现,您可以在一次grep调用中使用:

grep -P '^(?=.*pat1)(?!.*pat2)|^(?=.*pat2)(?!.*pat1)'
Run Code Online (Sandbox Code Playgroud)

即找到匹配pat1但不匹配pat2pat2不匹配的行pat1

(?=...)(?!...)分别是前瞻和负前瞻运算符。因此,从技术上讲,上面的内容查找主题 ( ^)的开头,前提是它后跟.*pat1而不是后跟.*pat2,或者与pat1pat2反转相同。

这对于包含两种模式的行来说是次优的,因为它们会被查找两次。您可以改为使用更高级的 perl 运算符,例如:

grep -P '^(?=.*pat1|())(?(1)(?=.*pat2)|(?!.*pat2))'
Run Code Online (Sandbox Code Playgroud)

(?(1)yespattern|nopattern)针对匹配yespattern,如果1第一捕获组(空()上文)相匹配,并nopattern以其他方式。如果()匹配,这意味着pat1不匹配,所以我们寻找pat2(正前方看),我们期待的不是 pat2否则(负前瞻)。

sed,你可以这样写:

sed -ne '/pat1/{/pat2/!p;d;}' -e '/pat2/p'
Run Code Online (Sandbox Code Playgroud)