Risposte:
Uno strumento diverso da quello grepè la strada da percorrere.
Usando perl, ad esempio, il comando sarebbe:
perl -ne 'print if /pattern1/ xor /pattern2/'
perl -neesegue il comando dato su ciascuna riga di stdin, che in questo caso stampa la riga se corrisponde /pattern1/ xor /pattern2/, o in altre parole corrisponde a un modello ma non all'altro (esclusivo o).
Questo funziona per il modello in entrambi gli ordini e dovrebbe avere prestazioni migliori rispetto a più invocazioni di grep, ed è anche meno digitante.
O, ancora più breve, con awk:
awk 'xor(/pattern1/,/pattern2/)'
o per le versioni di awk che non hanno xor:
awk '/pattern1/+/pattern2/==1`
xordisponibile solo in GNU Awk?
/pattern1/+/pattern2/==1ir xormancante.
\b) negli schemi stessi, cioè \bword\b.
Prova con 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- preferiscigrep -E
-fe le -eopzioni, anche se più vecchie egrepe fgrepcontinueranno a essere supportate per un po '.
grep(che supporta -F, -E, -e, -fcome richiede POSIX) è in /usr/xpg4/bin. Le utility in /binsono antiquate.
Con grepimplementazioni che supportano espressioni regolari simili a perl (like pcregrepo GNU o ast-open grep -P), puoi farlo in unagrep chiamata con:
grep -P '^(?=.*pat1)(?!.*pat2)|^(?=.*pat2)(?!.*pat1)'
Cioè trova le linee che corrispondono pat1ma non pat2, o pat2nopat1 .
(?=...)e (?!...)sono rispettivamente operatori del futuro e del futuro. Quindi tecnicamente, quanto sopra cerca l'inizio del soggetto ( ^) a condizione che sia seguito da .*pat1e non seguito da .*pat2, o lo stesso conpat1 e pat2invertito.
Questo non è ottimale per le linee che contengono entrambi i modelli come verrebbero ricercate due volte. È possibile invece utilizzare operatori perl più avanzati come:
grep -P '^(?=.*pat1|())(?(1)(?=.*pat2)|(?!.*pat2))'
(?(1)yespattern|nopattern)corrisponde a yespatternse il gruppo di acquisizione della 1st (vuoto ()sopra) corrisponde e in caso nopatterncontrario. Se ()corrisponde, significa pat1che non corrisponde, quindi cerchiamo pat2(sguardo positivo in avanti) e cerchiamo non pat2 altrimenti (negativo sguardo al futuro).
Con sed, potresti scriverlo:
sed -ne '/pat1/{/pat2/!p;d;}' -e '/pat2/p'
grep: the -P option only supports a single pattern, almeno su tutti i sistemi a cui ho accesso. +1 per la tua seconda soluzione, però.
grep. pcregrepe ast-open grep non ha questo problema. Ho sostituito il multiplo -econ l'operatore di alternanza RE, quindi ora dovrebbe funzionare anche con GNU grep.
In termini booleani, stai cercando A xor B, che può essere scritto come
(A e non B)
o
(B e non A)
Dato che la tua domanda non menziona il fatto che sei interessato all'ordine dell'output fintanto che vengono mostrate le linee corrispondenti, l'espansione booleana di A xor B è dannatamente semplice in 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.
Per il seguente esempio:
# Patterns:
# apple
# pear
# Example line
line="a_apple_apple_pear_a"
Questo può essere fatto esclusivamente con grep -E, uniqe wc.
# Grep for regex pattern, sort as unique, and count the number of lines
result=$(grep -oE 'apple|pear' <<< $line | sort -u | wc -l)
Se grepviene compilato con le espressioni regolari Perl, puoi abbinare l'ultima occorrenza invece di dover reindirizzare a uniq:
# Grep for regex pattern and count the number of lines
result=$(grep -oP '(apple(?!.*apple)|pear(?!.*pear))' <<< $line | wc -l)
Emetti il risultato:
# 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
Una fodera:
(($(grep -oP '(apple(?!.*apple)|pear(?!.*pear))' <<< $line | wc -l) == 1)) && echo Only one word matched
Se non si desidera codificare il modello, è possibile automatizzare con una funzione il montaggio con un set variabile di elementi.
Questo può anche essere fatto in modo nativo in Bash come una funzione senza pipe o processi aggiuntivi, ma sarebbe più coinvolto ed è probabilmente al di fuori dell'ambito della tua domanda.
Big apple\ne pear-shaped\n, l'output dovrebbe contenere entrambe le righe. La tua soluzione otterrebbe un conteggio di 2; la versione lunga riportava "Entrambe le parole abbinate" (che è una risposta alla domanda sbagliata) e la versione breve non diceva nulla. (3) Un suggerimento: usare -oqui è una pessima idea, perché nasconde le linee che contengono le corrispondenze, quindi non puoi vedere quando entrambe le parole appaiono sulla stessa linea. ... (proseguendo)
uniq/ sort -ue l'espressione regolare Perl per abbinare solo l'ultima occorrenza su ciascuna riga non aggiunge realmente una risposta utile a questa domanda. Ma, anche se lo facessero, sarebbe comunque una cattiva risposta perché non spieghi come contribuiscono a rispondere alla domanda. (Vedi la risposta di Stéphane Chazelas per un esempio di una buona spiegazione.)
[a-z][a-z0-9]\(,7\}\(\.[a-z0-9]\{,3\}\)+? (2) Cosa succede se una delle parole / motivi appare più di una volta in una riga (e l'altra non appare)? È equivalente alla parola che appare una volta o conta come ricorrenze multiple?