grepを使用して、両方ではなく2つの単語のいずれかを含む行を検索する方法


11

テキストファイルで 'word1' XOR 'word2'の行を検索したい。したがって、word1、word2の行は出力されますが、これらの両方の単語の行は出力されません。XORを使用したかったのですが、LinuxコマンドラインでXORを記述する方法がわかりません。

私は試した:

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

その他多数ありますが、成功することはできませんでした。

回答:


6

grep 'word1\|word2' text.txtword1またはを含む行を検索しますword2。これには、両方を含む行が含まれます。

grep word1 text.txt | grep word2含む行を検索word1してword2。二つの言葉は、(例えば重複することができますfoobar含まれていfooob)。両方の単語を含むが重複しない方法でのみ行を検索する別の方法は、次のいずれかの順序で検索することです。grep 'word1.*word2\|word2.*word1' text.txt

grep word1 text.txt | grep -v word2を含むword1が含まない行を検索しますword2。この-vオプションは、grepに、一致しない行を維持し、一致する行を削除するように指示します。これにより、期待した結果の半分が得られます。対称検索を追加すると、単語の1つだけを含むすべての行が得られます。

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'

ありがとう、これはまさに私が探していたものです。他の回答も非常に興味深いので、よく調べてください。皆様、ありがとうございました。
Lukali 2018

17

GNUの場合awk

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

またはポータブルに:

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

grepをサポートする-P(PCRE):

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

sed

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

単語全体だけを考慮したい場合(たとえば、その中にfoobar、その中にもない場合)、それらの単語の区切り方を決定する必要があります。多くの実装のオプションのように、文字、数字、アンダースコア以外の文字によるものである場合は、次のように変更します。foobarbarbar-wgrep

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

以下のためにsedそれはあなたが持っていない限り少し複雑になりsedGNUのような実装をsed サポートしていること\</ \>GNUのような単語の境界としてawk行います。


6
ステファン、シェルスクリプトについての本を書いてください!
pfnuesel 2018

申し訳ありませんが、数週間前にコマンドラインを開始しただけです。単語のみを検索するように強制するにはどうすればよいですか?-Pwと-wPを試しましたが、間違った出力が表示されました。また、* word1 / * word2とword1 / word2の間で ''を使用しようとしました。
ルカリ

@Lukali、編集を参照してください。
ステファンシャゼル

2

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.