user_unknown schrieb:
Das dritte ist ein zweiter Filter, nämlich head, und das nimmt nur 10 Zeilen aus der Pipeline entgegen. Hänge ich das hinten dran, dann tut sich am Bildschirm erst mal nichts, aber dann erscheint der Output in einem Rutsch:
| ./p1.sh | egrep "[2468]" | head
|
Erst head
und dann egrep
, und sie kommen auch auf einmal mit einem Rutsch nach Funkstille zu Beginn.
| ./p1.sh | head | egrep "[2468]"
|
Kann jemand erklären, wieso das so ist? Wieso kommen bei 2 Filtern die Daten nicht auch tröpfchenweise?
Wenn Du in der Variante mit zwei Filtern und egrep
als erstem Filter die Option "--line-buffered" für egrep
verwendest, dann tröpfeln die Ausgaben ebenfalls zeilenweise. head
hat so eine Option nicht, aber man kann als Ersatz sed -nue '1,10p'
oder sed '10q'
nutzen. Wenn man diesen Filter an der ersten Stelle hat und egrep
als zweiten, sieht man ebenfalls den Tröpfeleffekt.
Meine Vermutung ist, dass egrep
und head
bei der Ausgabe auf das Terminal nur zeilenweise Puffern, während sie bei der Ausgabe in eine Pipe einen größeren Puffer verwenden. Grund könnte sein, dass man innerhalb der Pipeline davon ausgeht, dass tendenziell eher größere Datenmengen bewegt werden, womit ein größerer Puffer die Sache deutlich effektiver macht. Bei der Ausgabe in ein Terminal hingegen geht man vermutlich eher davon aus, dass da ein Mensch lesen will, und bevorzugt schnelle Auslieferung, weil man davon ausgehen kann, dass eine Pipeline nur wenig ausgeben wird.
Man könnte mal den ersten Filter mit und ohne Pufferbeschränkung ausführen und mit strace
Ausgaben in eine Trace-Datei schreiben lassen. Ggf. kann man da schon sehen, was die Programme da treiben bzw. ob sie z.B. den Typ des ausgehenden Dateideskriptors abfragen.
ChickenLipsRfun2eat schrieb:
/ Nachtrag: Das mit „in einem Rutsch“ ist auch logisch, da head
ja auf die 10 Zeilen wartet.
Nein, das ist nicht logisch: im Gegensatz zu tail
kann head
gleich mit der Ausgabe beginnen, sobald sie kommt. Da muss nicht auf zehn Zeilen gewartet werden.
Das sleep ist da schon vorbei, das gilt ja nur während der Erzeugung.
Ich glaube, Du bist hier auf einem Holzweg: es wird ja parallel ausgeführt und nicht etwa sequentiell. Wie man sieht, setzt sich ja das Timingverhalten durch die Pipeline fort, wenn man lediglich einen Filter dranhängt.
ChickenLipsRfun2eat schrieb:
Ich versuche es nochmal im Klartext, weil ich mir nicht sicher bin, ob die Aussage klar wird 😉
Hm...
Wenn du x | head -n10
machst, wird die komplette Ausgabe von x nach 10 Zeilen abgeschnitten.
Korrekt.
Wenn du x | grep | head
machst, wirft grep erst nach Beenden das EOF, was nach abarbeiten der while-Schleife kommt.
Schon, aber das macht hier keinen bemerkbaren Unterschied, weil die Quelle sowieso nur 92 Bytes liefert. Das ist weit weniger als eine Speicherseite von 4k. Mit anderen Worten: sowohl head
als auch egrep
werden hier gleichermaßen Puffern und das "frühe" EOL von head
macht keinen Unterschied für das Tröpfeln. Es sollte allerdings einen machen, wann das Ergebnis ansteht, nämlich (34 - 10) * 0.1s = 2.4s früher. Und in der Tat:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | $ time ./quelle | ./f1 | ./f2
2
4
6
8
12
14
16
18
20
21
real 0m3,453s
user 0m0,039s
sys 0m0,023s
$ time ./quelle | ./f2 | ./f1
2
4
6
8
real 0m1,021s
user 0m0,020s
sys 0m0,009s
|
In beiden Fällen erfolgt allerdings die Ausgabe in einem Rutsch. Nebenbei sieht man hier auch sehr schön, dass bestimmte Filterkombinationen je nach Reihenfolge zu unterschiedlichen Ergebnissen führen. ☺
Der Effekt vom frühen EOL wird erst so richtig sichtbar, wenn man als Quelle sehr viele Daten schickt. Das kann man z.B. ausprobieren mit seq -f '%1000f' 1 1000000
:
| $ time seq -f '%1000f' 1 1000000
... tonnenweise Ausgabe ...
real 2m2,744s
user 0m4,164s
sys 0m8,459s
$ time seq -f '%1000f' 1 1000000 | head
... wenig Ausgabe ...
real 0m0,003s
user 0m0,000s
sys 0m0,004s
|
Aber, wie gesagt, das beeinflusst die Gesamtzeit, aber nicht, wie die Daten über die Zeit verteilt ausgegeben werden ("Tröpfeln").