回答:
以外のツールgrep
は、行く方法です。
たとえば、perlを使用すると、コマンドは次のようになります。
perl -ne 'print if /pattern1/ xor /pattern2/'
perl -ne
stdinの各行で指定されたコマンドを実行します。この場合、行がに一致する場合/pattern1/ xor /pattern2/
、または一方のパターンに一致するが他方のパターンには一致しない場合(排他的または)に出力します。
これはどちらの順序のパターンでも機能し、の複数の呼び出しよりもパフォーマンスが優れているはずでありgrep
、入力も少なくなります。
または、さらに短く、awkで:
awk 'xor(/pattern1/,/pattern2/)'
またはawkのバージョンがない場合xor
:
awk '/pattern1/+/pattern2/==1`
xor
はGNU Awkでのみ利用可能ですか?
/pattern1/+/pattern2/==1
ir xor
が欠落している場合、それを置き換えることができます。
\b
パターン自体に単語境界()を入れることができ\bword\b
ます。
で試す egrep
egrep 'pattern1|pattern2' file | grep -v -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'
grep -e foo -e bar | grep -v -e 'foo.*bar' -e 'bar.*foo'
Direct invocation as either egrep or fgrep is deprecated
-prefergrep -E
grep
(その担体は-F
、-E
、-e
、-f
POSIXが必要とされる)です/usr/xpg4/bin
。のユーティリティ/bin
は時代遅れのものです。
grep
perlのような正規表現(like pcregrep
またはGNUまたはast-open grep -P
)をサポートする実装を使用すると、次のように1回のgrep
呼び出しで実行できます。
grep -P '^(?=.*pat1)(?!.*pat2)|^(?=.*pat2)(?!.*pat1)'
それはそれと一致する行を見つけることでpat1
はなくpat2
、かpat2
ではなくpat1
。
(?=...)
そして(?!...)
、それぞれ先読み演算子と負の先読み演算子です。技術的には、上記の例では、件名の先頭(^
)が検索されますが、その後にが続き、.*pat1
後に続かない.*pat2
か、または同じでpat1
、pat2
逆になります。
2回検索されるため、両方のパターンを含む行には最適ではありません。代わりに、次のようなより高度なperl演算子を使用できます。
grep -P '^(?=.*pat1|())(?(1)(?=.*pat2)|(?!.*pat2))'
(?(1)yespattern|nopattern)
一致yespattern
であれば1
番目のキャプチャグループ(空の()
上で)一致し、nopattern
そうでありません。その場合は()
マッチ、手段があることpat1
と一致しませんでした、私たちが求めているので、pat2
(正ルック先)、そして私たちは探していない pat2
そうでない場合は(先にネガティブな外観)。
でsed
、あなたはそれを書くことができます:
sed -ne '/pat1/{/pat2/!p;d;}' -e '/pat2/p'
grep: the -P option only supports a single pattern
少なくとも私がアクセスできるすべてのシステムで、最初のソリューションはで失敗します。ただし、2番目のソリューションでは+1。
grep
です。pcregrep
ast-open grepにはその問題はありません。倍数-e
を代替RE演算子に置き換えたので、GNU grep
でも動作するはずです。
ブール用語では、次のように記述できるA xor Bを探しています。
(BではなくA)
または
(AではなくB)
一致する行が表示されている限り、出力の順序に関心があるという質問に言及していないことを考えると、A xor Bのブール展開はgrepではかなり単純です:
$ cat << EOF > foo
> a b
> a
> b
> c a
> c b
> b a
> b c
> EOF
$ grep -w 'a' foo | grep -vw 'b'; grep -w 'b' foo | grep -vw 'a';
a
c a
b
c b
b c
sort | uniq
。
次の例:
# Patterns:
# apple
# pear
# Example line
line="a_apple_apple_pear_a"
これは、純粋で行うことができgrep -E
、uniq
とwc
。
# Grep for regex pattern, sort as unique, and count the number of lines
result=$(grep -oE 'apple|pear' <<< $line | sort -u | wc -l)
grep
がPerlの正規表現でコンパイルされている場合、次の場所にパイプする必要はなく、最後に一致するものに一致させることができますuniq
。
# Grep for regex pattern and count the number of lines
result=$(grep -oP '(apple(?!.*apple)|pear(?!.*pear))' <<< $line | wc -l)
結果を出力します。
# Only one of the words exists if the result is < 2
((result > 0)) &&
if (($result < 2)); then
echo Only one word matched
else
echo Both words matched
fi
ワンライナー:
(($(grep -oP '(apple(?!.*apple)|pear(?!.*pear))' <<< $line | wc -l) == 1)) && echo Only one word matched
パターンをハードコーディングしたくない場合は、要素の可変セットでパターンを組み立てることを関数で自動化できます。
これは、パイプや追加プロセスのない関数としてBashでネイティブに行うこともできますが、より複雑になり、おそらく質問の範囲外になります。
Big apple\n
とpear-shaped\n
が含まれる場合、出力にはこれらの行の両方が含まれます。ソリューションのカウントは2になります。長いバージョンでは「両方の単語が一致しました」(これは間違った質問に対する答えです)と報告され、短いバージョンではまったく何も言われません。(3)提案:-o
ここを使用すると、一致する行が非表示になり、両方の単語が同じ行に表示されるのが見えなくなるため、非常に悪い考えです。…(続き)
uniq
/の使用sort -u
と、各行の最後の出現のみに一致する派手なPerl正規表現は、この質問に対する有用な答えにはなりません。しかし、たとえ彼らがそうしたとしても、質問への回答に彼らがどのように貢献するかを説明しないので、それはまだ悪い答えでしょう。(良い説明の例については、ステファン・チャゼラスの回答を参照してください。)
[a-z][a-z0-9]\(,7\}\(\.[a-z0-9]\{,3\}\)+
?などの正規表現 (2)単語/パターンの1つが1行に2回以上現れる(そしてもう1つが現れない)場合はどうなりますか?これは、一度出現する単語と同等ですか、それとも複数の出現としてカウントされますか?