
Das folgende Skript hat keinen anderen Zweck, als diese Frage zu veranschaulichen.
#!/usr/bin/env zsh
arbitrary_pipeline () {
shuf | tr a-z A-Z
}
tmpdir=$( mktemp -d )
mkfifo $tmpdir/{orig,alt}
{ tee $tmpdir/orig | arbitrary_pipeline > $tmpdir/alt; } &
pid=$!
paste $tmpdir/orig $tmpdir/alt
rm -rf $tmpdir
wait $pid
Das Skript verwendet tee
und zwei benannte Pipes, um einige (beliebige) Standardeingaben in zwei Streams aufzuteilen, leitet einen davon an eine beliebige Pipeline um und übergibt die resultierenden zwei Streams an die Eingaben paste
. Schematisch:
STDIN --- > --.- arbitrary_pipeline -.
\ \
paste `----<FIRST-ARGUMENT> `- <SECOND-ARGUMENT> --> STDOUT
(In diesem Fall arbitrary_pipeline
wird einfach die Standardeingabe neu gemischt und in Großbuchstaben umgewandelt, aber wie der Name schon sagt, könnte es alles Mögliche sein.)
Die Standardausgabe des Skripts sieht gut aus, aber der wait
Befehl schlägt immer fehl:
% grep -iP 'z.*s.*h' /usr/share/dict/words | /tmp/test.sh
Nietzsche CITIZENSHIP'S
Zubeneschamali NIETZSCHE
Zubeneschamali's ZUBENESCHAMALI
citizenship CITIZENSHIP
citizenship's ZUBENESCHAMALI'S
/tmp/test.sh:wait:18: pid 26357 is not a child of this shell
Was mache ich falsch?
Zur Info:
/usr/bin/env zsh --version
# zsh 5.0.7 (x86_64-pc-linux-gnu)
BEARBEITUNGEN:
- habe geschweifte Klammern um die
tee
Pipeline hinzugefügt, gemäß dem Vorschlag von jordanm. (Die Ergebnisse haben sich jedoch nicht geändert.) - ersetzt
&!
durch&
als Antwort auf den Kommentar von Stéphane Chazelas. (Auch hier haben sich die Ergebnisse nicht geändert.)
Antwort1
Vor 5.0.8 zsh
konnte nicht auf bereits tote Jobs gewartet werden. Das wurde 2014 in 5.0.8 geändert. Siehe die ÄnderungDort.
Hier können Sie stderr einfach umleiten, um /dev/null
das Problem zu ignorieren:
wait $pid 2> /dev/null
Beachten Sie Folgendes:
{ tee $tmpdir/orig | arbitrary_pipeline > $tmpdir/alt; } &
Als Optimierung zsh
wird dafür kein zusätzlicher Prozess aufgespalten arbitrary_pipeline
, sondern es wird im selben Prozess ausgeführt, der die im Hintergrund gestartete Subshell ausführt.
paste
wird nicht beendet, bevor es EOF auf seinem Standardeingang sieht, wobei sein Standardeingang die Pipe ist, in der $pid
(und seine untergeordneten Elemente, falls vorhanden) am anderen Ende schreiben. Es wird also kein EOF sehen, bis $pid
(und seine untergeordneten Elemente) alle seine Dateideskriptoren (normalerweise nur Standardausgang) zum schreibenden Ende der Pipe geschlossen hat. Sofern $pid
sein Standardausgang nicht explizit geschlossen wird (was sehr selten vorkommt), geschieht dies nur beim Beenden.
Das bedeutet paste
, dass der Wille in den meisten Fällen nicht vorher beendet wird. $pid
Es ist jedoch wait
für alle Fälle trotzdem eine gute Idee, dies zu tun.
Beachten Sie, dass Sie hier Folgendes verwenden könnten, coproc
um die temporären FIFOs zu vermeiden:
coproc arbitrary_pipeline
cat >&p | paste - /dev/fd/3 3<&p &
coproc : close
wait
(beachten Sie, dass wait
auch auf das coproc
s gewartet wird).