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 -ne
esegue 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`
xor
disponibile solo in GNU Awk?
/pattern1/+/pattern2/==1
ir xor
mancante.
\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
-f
e le -e
opzioni, anche se più vecchie egrep
e fgrep
continueranno a essere supportate per un po '.
grep
(che supporta -F
, -E
, -e
, -f
come richiede POSIX) è in /usr/xpg4/bin
. Le utility in /bin
sono antiquate.
Con grep
implementazioni che supportano espressioni regolari simili a perl (like pcregrep
o GNU o ast-open grep -P
), puoi farlo in unagrep
chiamata con:
grep -P '^(?=.*pat1)(?!.*pat2)|^(?=.*pat2)(?!.*pat1)'
Cioè trova le linee che corrispondono pat1
ma non pat2
, o pat2
nopat1
.
(?=...)
e (?!...)
sono rispettivamente operatori del futuro e del futuro. Quindi tecnicamente, quanto sopra cerca l'inizio del soggetto ( ^
) a condizione che sia seguito da .*pat1
e non seguito da .*pat2
, o lo stesso conpat1
e pat2
invertito.
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 yespattern
se il gruppo di acquisizione della 1
st (vuoto ()
sopra) corrisponde e in caso nopattern
contrario. Se ()
corrisponde, significa pat1
che 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
. pcregrep
e ast-open grep non ha questo problema. Ho sostituito il multiplo -e
con 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
, uniq
e 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 grep
viene 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\n
e 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 -o
qui è 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 -u
e 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?