Ich würde gerne wissen, ob es eine Möglichkeit gibt, eine Reihe von grep-Anweisungen zu kombinieren, bei denen der Effekt darin besteht, die Ausdrücke „und“ statt „oder“ die übereinstimmenden Ausdrücke zu verwenden.
Demo unten:
./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
Hören Sie sich das Skript an.
#!/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"
Akzeptierte Antwort:
Sie können den Filter grep a | grep c | grep -v d
zu einem einzigen einfachen grep
. Es gibt nur komplizierte und uneffektive Wege. Das Ergebnis hat eine langsame Leistung und die Bedeutung des Ausdrucks ist verschleiert.
Einzelne Befehlskombination der drei Greps
Wenn Sie nur einen einzelnen Befehl ausführen möchten, können Sie awk
verwenden das auch mit regulären Ausdrücken funktioniert und diese mit logischen Operatoren kombinieren kann. Hier ist das Äquivalent Ihres Filters:
awk '/a/ && /c/ && $0 !~ /d/'
Ich denke, in den meisten Fällen gibt es keinen Grund, eine Pipe zu einem einzelnen Befehl zu vereinfachen, außer wenn die Kombination zu einem relativ einfachen grep-Ausdruck führt, der schneller sein könnte (siehe Ergebnisse unten).
Unix-ähnliche Systeme sind darauf ausgelegt, Pipes zu verwenden und verschiedene Dienstprogramme miteinander zu verbinden. Die Rohrkommunikation ist zwar nicht die effektivste, aber in den meisten Fällen ausreichend. Da heutzutage die meisten neuen Computer mehrere CPU-Kerne haben, können Sie die CPU-Parallelisierung „natürlich“ nutzen, indem Sie einfach eine Pipe verwenden!
Ihr ursprünglicher Filter funktioniert sehr gut und ich denke, dass in vielen Fällen der awk
Lösung wäre selbst auf einem einzelnen Kern etwas langsamer.
Leistungsvergleich
Mit einem einfachen Programm habe ich aus den Zeichen a
eine zufällige Testdatei mit 200 000 000 Zeilen zu je 4 Zeichen als Zufallskombination generiert , b
, c
und d
. Die Datei hat 1 GB. Während der Tests wurde es vollständig in den Cache geladen, sodass keine Festplattenoperationen die Leistungsmessung beeinflussten. Die Tests wurden auf Intel Dual Core ausgeführt.
Single grep
$ 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
Single awk
$ time ( awk '/a/ && /c/ && $0 !~ /d/' testfile >/dev/null )
real 0m54.088s
user 0m53.755s
sys 0m0.304s
Die ursprünglichen drei Greps wurden geleitet
$ time ( grep a testfile | grep c | grep -v d >/dev/null )
real 0m28.794s
user 0m52.715s
sys 0m1.072s
Hybrid – positive Greps kombiniert, negative Pipe
$ time ( grep -E 'a.*c|c.*a' testfile | grep -v d >/dev/null )
real 0m15.838s
user 0m24.998s
sys 0m0.676s
Hier sehen Sie, dass das einzelne grep
ist aufgrund des komplexen Ausdrucks sehr langsam. Die ursprüngliche Pipe von drei greps ist wegen einer guten Parallelisierung ziemlich schnell. Ohne Parallelisierung – auf einem einzelnen Kern – läuft die ursprüngliche Pipe nur geringfügig schneller als awk
der als einzelner Prozess nicht parallelisiert wird. Awk und grep verwenden wahrscheinlich denselben Code für reguläre Ausdrücke und die Logik der beiden Lösungen ist ähnlich.
Der klare Gewinner ist die Hybrierung, die zwei positive Greps kombiniert und das negative in der Pfeife lässt. Es scheint, dass der reguläre Ausdruck mit |
hat keine Leistungseinbußen.