Vorrei sapere se esiste un modo per combinare una serie di istruzioni grep in cui l'effetto è "e" le espressioni piuttosto che "o" le espressioni corrispondenti.
Demo qui sotto:
./script
From one grep statement, I want output like this
a b c
not like this
a
c
a b
a b c
a b c d
Ascolta è uno sguardo alla sceneggiatura.
#!/bin/bash
string="a
b
c
d
a b
a b c
a b c d"
echo -e "\t From one grep statement I want output like this"
echo "$string" |
grep a |grep c |grep -v d #Correct output but pipes three grep statements
echo -e "\n\tNot like this"
echo "$string" |
grep -e'a' -e'c' -e-v'd' #One grep statement but matching expressions are "or" versus "and"
Risposta accettata:
Non puoi trasformare il filtro grep a | grep c | grep -v d
a un singolo semplice grep
. Ci sono solo modi complicati e inefficaci. Il risultato ha prestazioni lente e il significato dell'espressione è oscurato.
Combinazione di comando singolo dei tre greps
Se vuoi eseguire un solo comando puoi usare awk
che funziona anche con le espressioni regolari e può combinarle con operatori logici. Ecco l'equivalente del tuo filtro:
awk '/a/ && /c/ && $0 !~ /d/'
Penso che nella maggior parte dei casi non ci sia motivo per semplificare una pipe in un singolo comando tranne quando la combinazione risulta in un'espressione grep relativamente semplice che potrebbe essere più veloce (vedi risultati sotto).
I sistemi simili a Unix sono progettati per utilizzare tubi e per collegare tra loro varie utenze. Sebbene la comunicazione tramite pipe non sia la più efficace possibile, nella maggior parte dei casi è sufficiente. Poiché al giorno d'oggi la maggior parte dei nuovi computer ha più core CPU, puoi utilizzare "naturalmente" la parallelizzazione della CPU semplicemente usando una pipe!
Il tuo filtro originale funziona molto bene e penso che in molti casi il awk
la soluzione sarebbe un po' più lenta anche su un singolo core.
Confronto delle prestazioni
Utilizzando un semplice programma ho generato un file di test casuale con 200 000 000 righe, ciascuna con 4 caratteri come combinazione casuale dai caratteri a
, b
, c
e d
. Il file ha 1 GB. Durante i test è stato completamente caricato nella cache, quindi nessuna operazione sul disco ha influito sulla misurazione delle prestazioni. I test sono stati eseguiti su Intel dual core.
Grep singolo
$ time ( grep -E '^[^d]*a[^d]*c[^d]*$|^[^d]*c[^d]*a[^d]*$' testfile >/dev/null )
real 3m2.752s
user 3m2.411s
sys 0m0.252s
Singolo awk
$ time ( awk '/a/ && /c/ && $0 !~ /d/' testfile >/dev/null )
real 0m54.088s
user 0m53.755s
sys 0m0.304s
I tre grep originali convogliati
$ time ( grep a testfile | grep c | grep -v d >/dev/null )
real 0m28.794s
user 0m52.715s
sys 0m1.072s
Ibrido:greps positivi combinati, convogliamento negativo
$ time ( grep -E 'a.*c|c.*a' testfile | grep -v d >/dev/null )
real 0m15.838s
user 0m24.998s
sys 0m0.676s
Qui vedi che il singolo grep
è molto lento a causa dell'espressione complessa. La pipe originale di tre greps è piuttosto veloce grazie a una buona parallelizzazione. Senza parallelizzazione, su un singolo core, la pipe originale funziona leggermente più veloce di awk
che come un singolo processo non è parallelizzato. Awk e grep probabilmente utilizzano lo stesso codice delle espressioni regolari e la logica delle due soluzioni è simile.
Il chiaro vincitore è l'hybring che combina due grep positivi e lascia quello negativo nella pipe. Sembra che l'espressione regolare con |
non ha alcuna penalità di prestazione.