Wartebefehl fehlgeschlagen

Wartebefehl fehlgeschlagen

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 teeund 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_pipelinewird 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 waitBefehl 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:

  1. habe geschweifte Klammern um die teePipeline hinzugefügt, gemäß dem Vorschlag von jordanm. (Die Ergebnisse haben sich jedoch nicht geändert.)
  2. 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 zshkonnte 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/nulldas Problem zu ignorieren:

wait $pid 2> /dev/null

Beachten Sie Folgendes:

{ tee $tmpdir/orig | arbitrary_pipeline > $tmpdir/alt; } &

Als Optimierung zshwird 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.

pastewird 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 $pidsein 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. $pidEs ist jedoch waitfür alle Fälle trotzdem eine gute Idee, dies zu tun.

Beachten Sie, dass Sie hier Folgendes verwenden könnten, coprocum die temporären FIFOs zu vermeiden:

coproc arbitrary_pipeline
cat >&p | paste - /dev/fd/3 3<&p &
coproc : close
wait

(beachten Sie, dass waitauch auf das coprocs gewartet wird).

verwandte Informationen