Odpowiedzi:
Narzędzie inne niż grep
jest do zrobienia.
Na przykład, używając perla, komenda wyglądałaby następująco:
perl -ne 'print if /pattern1/ xor /pattern2/'
perl -ne
uruchamia polecenie podane dla każdej linii standardowej, która w tym przypadku wypisuje linię, jeśli pasuje /pattern1/ xor /pattern2/
, lub innymi słowy dopasowuje jeden wzór, ale nie drugi (wyłączny lub).
Działa to dla wzorca w dowolnej kolejności i powinno mieć lepszą wydajność niż wiele wywołań grep
, a także mniej pisania.
Lub, nawet krócej, z awk:
awk 'xor(/pattern1/,/pattern2/)'
lub dla wersji awk, które nie mają xor
:
awk '/pattern1/+/pattern2/==1`
xor
dostępny tylko w GNU Awk?
/pattern1/+/pattern2/==1
ir xor
.
\b
) w samych wzorcach, tj \bword\b
.
Spróbuj z 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
- preferujgrep -E
-f
i -e
opcje, chociaż są starsze egrep
i fgrep
będą przez jakiś czas obsługiwane.
grep
(to podpory -F
, -E
, -e
, -f
jak tego wymaga POSIX) jest /usr/xpg4/bin
. Narzędzia w /bin
są przestarzałe.
Dzięki grep
implementacjom, które obsługują wyrażenia regularne podobne do perla (jak pcregrep
lub GNU lub ast-open grep -P
), możesz to zrobić za pomocą jednego grep
wywołania za pomocą:
grep -P '^(?=.*pat1)(?!.*pat2)|^(?=.*pat2)(?!.*pat1)'
To jest znaleźć linie, które pasują, pat1
ale nie pat2
, pat2
ale nie pat1
.
(?=...)
i (?!...)
są odpowiednio operatorami perspektywicznymi i negatywnymi. Tak więc technicznie powyższe wygląda na początek tematu ( ^
) pod warunkiem, że następuje po nim .*pat1
i nie następuje po nim .*pat2
, lub to samo z pat1
i pat2
odwrócone.
Jest to nieoptymalne dla linii zawierających oba wzorce, ponieważ byłyby wówczas dwukrotnie wyszukiwane. Zamiast tego możesz użyć bardziej zaawansowanych operatorów perla, takich jak:
grep -P '^(?=.*pat1|())(?(1)(?=.*pat2)|(?!.*pat2))'
(?(1)yespattern|nopattern)
pasuje do, yespattern
jeśli grupa przechwytywania 1
st (pusta ()
powyżej) jest zgodna, i nopattern
inaczej. Jeśli to ()
mecze, że środki pat1
nie są zgodne, więc szukamy pat2
(pozytywnego spojrzenia w przyszłość) i patrzymy na nie pat2
inaczej (negatywny patrzeć w przyszłość).
Za pomocą sed
możesz to napisać:
sed -ne '/pat1/{/pat2/!p;d;}' -e '/pat2/p'
grep: the -P option only supports a single pattern
, przynajmniej na każdym systemie, do którego mam dostęp. +1 za drugie rozwiązanie.
grep
. pcregrep
a ast-open grep nie ma tego problemu. Zamieniłem wielokrotność -e
na alternatywny operator RE, więc powinien on działać również z GNU grep
.
Mówiąc logicznie, szukasz A xor B, który można zapisać jako
(A i nie B)
lub
(B i nie A)
Biorąc pod uwagę, że twoje pytanie nie wspomina, że zajmujesz się kolejnością danych wyjściowych, o ile wyświetlane są pasujące linie, rozszerzenie boolowskie A xor B jest dość proste w 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
.
Dla następującego przykładu:
# Patterns:
# apple
# pear
# Example line
line="a_apple_apple_pear_a"
Można to zrobić wyłącznie z grep -E
, uniq
, i wc
.
# Grep for regex pattern, sort as unique, and count the number of lines
result=$(grep -oE 'apple|pear' <<< $line | sort -u | wc -l)
Jeśli grep
jest skompilowany z wyrażeniami regularnymi Perla, możesz dopasować do ostatniego wystąpienia, zamiast konieczności potoku do uniq
:
# Grep for regex pattern and count the number of lines
result=$(grep -oP '(apple(?!.*apple)|pear(?!.*pear))' <<< $line | wc -l)
Wynik:
# 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
Jednowarstwowy:
(($(grep -oP '(apple(?!.*apple)|pear(?!.*pear))' <<< $line | wc -l) == 1)) && echo Only one word matched
Jeśli nie chcesz na stałe kodować wzorca, składanie go ze zmiennym zestawem elementów można zautomatyzować za pomocą funkcji.
Można to również zrobić natywnie w Bash jako funkcję bez rur lub dodatkowych procesów, ale byłby bardziej zaangażowany i prawdopodobnie nie wchodzi w zakres twojego pytania.
Big apple\n
i pear-shaped\n
, następnie wyjście powinno zawierać zarówno tych linii. Twoje rozwiązanie uzyskałoby liczbę 2; w długiej wersji będzie napisane „Dopasowane oba słowa” (co jest odpowiedzią na złe pytanie), a w krótkiej wersji nic nie powie. (3) Sugestia: użycie -o
tutaj jest naprawdę złym pomysłem, ponieważ ukrywa wiersze zawierające dopasowania, więc nie możesz zobaczyć, kiedy oba słowa pojawiają się w tym samym wierszu. … (Ciąg dalszy)
uniq
/ sort -u
i fantazyjnego wyrażenia regularnego Perla, by dopasować tylko ostatnie wystąpienie w każdym wierszu, tak naprawdę nie stanowi użytecznej odpowiedzi na to pytanie. Ale nawet gdyby tak było, nadal byłaby to zła odpowiedź, ponieważ nie wyjaśnisz, w jaki sposób przyczyniają się do udzielenia odpowiedzi na pytanie. (Zobacz odpowiedź Stéphane'a Chazelasa na przykład dobrego wyjaśnienia.)
[a-z][a-z0-9]\(,7\}\(\.[a-z0-9]\{,3\}\)+
? (2) Co się stanie, jeśli jedno ze słów / wzorów pojawi się więcej niż jeden raz w linii (a drugie nie pojawi się)? Czy jest to równoważne słowu występującemu raz, czy też liczy się jako wielokrotne wystąpienie?