Antworten:
Ein anderes Werkzeug als grepder richtige Weg.
Unter Verwendung von Perl wäre der Befehl beispielsweise:
perl -ne 'print if /pattern1/ xor /pattern2/'
perl -neFührt den Befehl aus, der über jede Zeile von stdin ausgegeben wird. In diesem Fall wird die Zeile gedruckt, wenn sie übereinstimmt /pattern1/ xor /pattern2/, oder mit anderen Worten , wenn sie mit einem Muster übereinstimmt, jedoch nicht mit dem anderen (exklusiv oder).
Dies funktioniert für das Muster in jeder Reihenfolge und sollte eine bessere Leistung aufweisen als mehrere Aufrufe von grepund ist auch weniger tippend.
Oder noch kürzer mit awk:
awk 'xor(/pattern1/,/pattern2/)'
oder für Versionen von awk, die nicht haben xor:
awk '/pattern1/+/pattern2/==1`
xornur in GNU Awk?
/pattern1/+/pattern2/==1ir ersetzen xorfehlt.
\b) in die Muster selbst einfügen, dh \bword\b.
Versuche es mit 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
-fund -eOptionen haben sollte , obwohl die älteren egrepund fgrepnoch eine Weile unterstützt werden.
grep(das Stützen -F, -E, -e, -fwie POSIX erfordert) ist in /usr/xpg4/bin. Die Versorgungsunternehmen in /binsind veraltet.
Mit grepImplementierungen, die perlähnliche reguläre Ausdrücke (wie pcregrepoder GNU oder ast-open grep -P) unterstützen, können Sie dies in einem grepAufruf tun :
grep -P '^(?=.*pat1)(?!.*pat2)|^(?=.*pat2)(?!.*pat1)'
Das heißt die Zeilen finden, die pat1aber nicht passen pat2, oder pat2doch nicht pat1.
(?=...)und (?!...)sind jeweils vorausschauende und negative vorausschauende Betreiber. Technisch betrachtet, sucht das Obige nach dem Anfang des Themas ( ^), vorausgesetzt, es wird gefolgt .*pat1und nicht gefolgt .*pat2, oder dasselbe mit pat1und pat2umgekehrt.
Das ist suboptimal für Linien, die beide Muster enthalten, da sie dann zweimal gesucht würden. Sie könnten stattdessen fortgeschrittenere Perl-Operatoren verwenden, wie:
grep -P '^(?=.*pat1|())(?(1)(?=.*pat2)|(?!.*pat2))'
(?(1)yespattern|nopattern)Übereinstimmungen mit, yespatternwenn die 1st- Erfassungsgruppe ( ()oben leer ) übereinstimmt, nopatternandernfalls. Wenn dies ()zutrifft, bedeutet pat1dies , dass es nicht zutrifft, und wir suchen nach pat2(positiver Blick nach vorn) und wir suchen nach nichts anderem pat2 (negativer Blick nach vorn).
Mit sedkönnte man es schreiben:
sed -ne '/pat1/{/pat2/!p;d;}' -e '/pat2/p'
grep: the -P option only supports a single pattern, zumindest auf jedem System, auf das ich Zugriff habe. +1 für Ihre zweite Lösung.
grep. pcregrepund ast-open grep haben dieses problem nicht. Ich habe das Multiple -edurch den alternierenden RE-Operator ersetzt, daher sollte es grepjetzt auch mit GNU funktionieren .
In Booleschen Begriffen suchen Sie nach A xor B, das als geschrieben werden kann
(A und nicht B)
oder
(B und nicht A)
Da in Ihrer Frage nicht erwähnt wird, dass Sie sich mit der Reihenfolge der Ausgabe befassen, solange die übereinstimmenden Zeilen angezeigt werden, ist die Boolesche Erweiterung von A xor B in grep ziemlich einfach:
$ 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.
Für das folgende Beispiel:
# Patterns:
# apple
# pear
# Example line
line="a_apple_apple_pear_a"
Dies kann rein mit getan werden grep -E, uniqund wc.
# Grep for regex pattern, sort as unique, and count the number of lines
result=$(grep -oE 'apple|pear' <<< $line | sort -u | wc -l)
Wenn grepes mit regulären Perl-Ausdrücken kompiliert wurde, können Sie beim letzten Vorkommen eine Übereinstimmung finden, anstatt eine Pipe an uniqfolgende Stellen ausführen zu müssen :
# Grep for regex pattern and count the number of lines
result=$(grep -oP '(apple(?!.*apple)|pear(?!.*pear))' <<< $line | wc -l)
Ergebnis ausgeben:
# 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
Ein Einzeiler:
(($(grep -oP '(apple(?!.*apple)|pear(?!.*pear))' <<< $line | wc -l) == 1)) && echo Only one word matched
Wenn Sie das Muster nicht hart codieren möchten, können Sie es mit einer Funktion automatisieren, indem Sie es mit einem variablen Satz von Elementen zusammensetzen.
Dies kann auch nativ in Bash als Funktion ohne Pipes oder zusätzliche Prozesse erfolgen, ist jedoch aufwändiger und liegt wahrscheinlich außerhalb des Rahmens Ihrer Frage.
Big apple\nund enthält pear-shaped\n, sollte die Ausgabe beide Zeilen enthalten. Ihre Lösung würde mit 2 bewertet; Die Langfassung würde "Beide Wörter stimmen überein" (was eine Antwort auf die falsche Frage ist) und die Kurzfassung würde überhaupt nichts aussagen. (3) Ein Vorschlag: Die Verwendung von -ohier ist eine wirklich schlechte Idee, da hier die Zeilen ausgeblendet werden, die die Übereinstimmungen enthalten, sodass Sie nicht sehen können, wann beide Wörter in derselben Zeile erscheinen. … (Fortsetzung)
uniq/ sort -uund der ausgefallene reguläre Perl-Ausdruck, der nur dem letzten Vorkommen in jeder Zeile entspricht, ergeben keine wirklich nützliche Antwort auf diese Frage. Aber selbst wenn dies der Fall wäre, wäre es immer noch eine schlechte Antwort, da Sie nicht erklären, wie sie zur Beantwortung der Frage beitragen. ( Ein Beispiel für eine gute Erklärung finden Sie in der Antwort von Stéphane Chazelas .)
[a-z][a-z0-9]\(,7\}\(\.[a-z0-9]\{,3\}\)+? (2) Was ist, wenn eines der Wörter / Muster mehr als einmal in einer Zeile erscheint (und das andere nicht)? Entspricht das dem Wort, das einmal vorkommt, oder zählt es als Mehrfachvorkommen?