如何使用 grep 搜索包含两个单词之一而不是同时包含两个单词的行?

如何使用 grep 搜索包含两个单词之一而不是同时包含两个单词的行?

我想在文本文件中搜索带有“word1”XOR“word2”的行。因此它应该输出包含 word1、word2 的行,但不输出包含这两个单词的行。我想使用 XOR,但我不知道如何在 Linux 命令行中编写它。

我试过:

grep 'word1\|word2' text.txt
grep word1 word2 text.txt
grep word1 text.txt | grep word2
grep 'word1\^word2' text.txt

等等,但都未能成功。

答案1

使用 GNU awk

$ printf '%s\n' {foo,bar}{bar,foo} neither | gawk 'xor(/foo/,/bar/)'
foofoo
barbar

或者便携式:

awk '((/foo/) + (/bar/)) % 2'

支持( grepPCRE -P):

grep -P '^((?=.*foo)(?!.*bar)|(?=.*bar)(?!.*foo))'

sed

sed '
  /foo/{
    /bar/d
    b
  }
  /bar/!d'

如果您只想考虑整个单词(例如既没有foo也没有barfoobarbarbar中),您需要决定如何分隔这些单词。如果它是由除字母、数字和下划线之外的任何字符组成的,就像-w许多grep实现的选项一样,那么您可以将它们更改为:

gawk 'xor(/\<foo\>/,/\<bar\>/)'
awk '((/(^|[^[:alnum:]_)foo([^[:alnum:]_]|$)/) + \
      (/(^|[^[:alnum:]_)bar([^[:alnum:]_]|$)/)) % 2'
grep -P '^((?=.*\bfoo\b)(?!.*\bbar\b)|(?=.*\bbar\b)(?!.*\bfoo\b))'

因为sed这会变得有点复杂,除非你有sed像 GNU 这样的实现,像 GNU 一样sed 支持\</\>作为字边界awk

答案2

grep 'word1\|word2' text.txt搜索包含word1或 的行word2。这包括同时包含两者的行。

grep word1 text.txt | grep word2搜索包含word1和 的行word2。这两个词可以重叠(例如foobarcontainsfooob)。搜索包含两个单词的行的另一种方法(但仅以不重叠的方式)是按任一顺序搜索它们:grep 'word1.*word2\|word2.*word1' text.txt

grep word1 text.txt | grep -v word2搜索包含word1但不包含 的行word2。该-v选项告诉 grep 保留不匹配的行并删除匹配的行,而不是相反。这将为您提供所需结果的一半。通过添加对称搜索,您可以获得恰好包含某个单词的所有行。

grep word1 text.txt | grep -v word2
grep word2 text.txt | grep -v word1

或者,您可以从包含任一单词的行开始,然后删除包含这两个单词的行。考虑到上面的构建块,如果单词不重叠,这很容易。

grep 'word1\|word2' text.txt | grep -v 'word1.*word2\|word2.*word1'

答案3

bash 解决方案:

#!/bin/bash 
while (( $# )); do
    a=0 ; [[ $1 =~ foo ]] && a=1 
    b=0 ; [[ $1 =~ bar ]] && b=1
    (( a ^ b )) && echo "$1"
    shift
done

要测试它:

$ ./script {foo,bar}\ {foo,bar} neither
foo foo
bar bar

相关内容