Hallo, Am Die, 19 Okt 2010, Andre Tann schrieb:
Marko Käning, Dienstag 19 Oktober 2010:
Weil das Verarbeiten von Regexps prinzipiell eine langsamere Angelegenheit ist. Das grep-Kommando ist speziell dafür optimiert, einzelne Zeilen rauszusuchen, sed hingegen muss alles interpretieren.
Das würde bedeuten, daß die an sich weniger eleganten Ketten grep | sed | awk... durchaus ihre Berechtigung haben. Der Preis für den Aufruf der Programme scheint niedriger zu sein als der Vorteil, den man durch die Spezialisierung wieder rausholt.
Nein. Du mißt Mist.
# bash -c "time awk '/delay=/ {print}' /var/log/mail > /dev/null" real 0m0.254s [..]
# bash -c "time sed '/delay=/p' /var/log/mail > /dev/null" real 0m0.225s [..]
# bash -c "time grep delay= /var/log/mail > /dev/null" real 0m0.017s [..]
Alles < 1s Gesamtlaufzeit hat kaum mehr Aussagekraft als wenn du die Laufzeiten von deinem Kater auswürfeln läßt. Und dann ist da noch der Einfluß vom Plattencache! Sinnvoll wird's IMHO ab 5s Laufzeit. Kurzum: deine Aufgabe ist zu klein. $ ls -lh /var/log/mail -rw-r----- 1 root root 1.7M Oct 19 12:22 /var/log/mail # chgrp dh /var/log/mail $ { for i in 1 2 3 4 5; do time cat /var/log/mail >/dev/null; d ; } 2>&1 | grep real real 0m0.028s real 0m0.016s real 0m0.039s real 0m0.028s real 0m0.025s [NB: da war die Datei (und cat) schon im Cache] $ loops=$(seq 100); $ { time for i in $loops ; do grep 'relay=' /var/log/mail | \ grep 'delay=' >/dev/null; done; } 2>&1 | grep real real 0m6.501s $ { time for i in $loops ; do fgrep 'relay=' /var/log/mail | \ fgrep 'delay=' >/dev/null; done; } 2>&1 | grep real real 0m6.459s $ { time for i in $loops ; do grep 'relay=' /var/log/mail | \ sed -n '/delay=/p' >/dev/null; done; } 2>&1 | grep real real 0m25.182s $ { time for i in $loops ; do sed -n '/relay=.*delay=/p' \ /var/log/mail >/dev/null; done; } 2>&1 | grep real real 0m46.968s $ { time for i in $loops ; do sed -n '/relay=/{/delay=/p;}' \ /var/log/mail >/dev/null; done; } 2>&1 | grep real real 1m5.630s Hm, das wundert mich jetzt. Kann aber auch an meinem uralt-sed liegen. $ { time for i in $loops ; do awk '/relay=/{ if(/delay=/){ print;}}' \ /var/log/mail >/dev/null; done; } 2>&1 | grep real real 0m11.321s Das wieder nicht. Aber jetzt wird's erst interessant. Man will ja anscheinend nicht nur die ganze Zeile, sondern nur das "delay". Bei mir im Log sieht das eh anders aus, da steht das delay ne Zeile später als das relay. Machen's wir also anders, und pfriemeln das relay aus der Zeile: $ loops=$(seq 25) $ { time for i in $loops ; do fgrep 'relay=' /var/log/mail | \ sed s/.*relay=\([^,]*\).*/\1/p' > /dev/null; done; } 2>&1 | grep real real 0m11.415s $ { time for i in $loops ; do grep 'relay=' /var/log/mail | \ sed s/.*relay=\([^,]*\).*/\1/p' > /dev/null; done; } 2>&1 | grep real real 0m11.507s [BTW: die 2 Werte schwanken recht stark, hab bei beiden auch >14s gehabt] $ { time for i in $loops ; do sed -n '/relay=/s/.*relay=\([^,]*\).*/\' \ /var/log/mail > /dev/null; done; } 2>&1 | grep real real 0m20.074s Hm, mein sed scheint echt ne 'matching-Schwäche' zu haben ... $ { time for i in $loops; do awk '/relay=/{ \ print gensub(/.*relay([^,]*).*/,"\\1","g"); }' /var/log/mail >/dev/null ; done; } 2>&1 | grep real real 0m14.173s Wobei das mit dem 'gensub' nicht die ideale Lösung ist, aber das '\1' korrekt zu übergeben ist auf der Kommandozeile arg mühsam. Ein awk-Script vereinfacht da dann vieles. $ export loops; $ { time perl -e ' foreach(split("\n",$ENV{loops})) { open(L, "<", $ARGV[0]) or die "$!\n"; while(<L>) { m/relay=/ and s/.*relay=([^,]*).*/$1/ and print; } close(L) or die "$!\n" }' /var/log/mail >/dev/null; } 2>&1 | grep real real 0m6.781s Ei, gugg emoll dooo! :-P (und ohne denn Looping säh's noch einfacher aus, z.B: perl -ne 'm/relay=/ and s/.*relay=([^,]*).*/$1/ and print;' *.log (dann macht perl das über-die-Dateien-iterieren selber ;) B::Deparse macht daraus übrigens: perl -e 'while (defined($_ = <ARGV>)) { print $_ if /relay=/ and s/.*relay=([^,]*).*/$1/; }' *.log Wie immer: "Know thy tools!" Daß sed beim pattern-matching echt so lahm ist, ist mir neu (und ich hab jetzt nur mit dem ollen hier getestet). $ bash --version GNU bash, version 2.03.0(1)-release (i386-suse-linux) $ grep --version grep (GNU grep) 2.2 $ sed --version GNU sed version 3.02 $ awk --version GNU Awk 3.0.4 $ perl --version This is perl, v5.10.0 built for i686-linux-thread-multi-64int-ld Ist insofern ein bissl unfair ggü. den ollen GNU tools ;) Ah, läuft doch (ohne Verrenkungen) im zu grep/sed/awk passend alten perl: { time perl5.00503 -e ' foreach(split("\n",$ENV{loops})) { open(L, "<$ARGV[0]") or die "$!\n"; while(<L>) { m/relay=/ and s/.*relay=([^,]*).*/$1/ and print; } close(L) or die "$!\n" }' /var/log/mail >/dev/null; } real 0m10.564s $ perl5.00503 --version This is perl, version 5.005_03 built for i586-linux Und ebenfalls wie immer: manchmal (also wenn die Gesamtlaufzeit eines Scriptes relevant wird) muß man eben selber am konkreten Fall testen (und zwar wenn irgend möglich mit realen Daten!!!). Und wie ich "Scripte" kenne, bleibt es eben *nicht* beim simplen grep | sed 's/a/b/' Sondern man macht noch mehr. Und dann landet man recht schnell an dem Punkt, wo man besser gleich alles in sed/awk/perl (in der Reihenfolge) macht ;) (bei perl dann gern auch gleich mal das 'find ...' per File::Find, wobei einem das nette Tool 'find2perl' auch noch hilft ;) HTH, -dnh -- So Linus, what are we doing tonight? The same thing we do every night Tux. Try to take over the world! -- Um die Liste abzubestellen, schicken Sie eine Mail an: opensuse-de+unsubscribe@opensuse.org Um eine Liste aller verfuegbaren Kommandos zu bekommen, schicken Sie eine Mail an: opensuse-de+help@opensuse.org