
以下のスクリプトは、この質問を説明すること以外の目的はありません。
#!/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
このスクリプトはtee
、 と 2 つの名前付きパイプを使用して、いくつかの (任意の) 標準入力を 2 つのストリームに分割し、そのうちの 1 つを任意のパイプラインにリダイレクトして、結果として得られた 2 つのストリームを の入力に渡しますpaste
。概略:
STDIN --- > --.- arbitrary_pipeline -.
\ \
paste `----<FIRST-ARGUMENT> `- <SECOND-ARGUMENT> --> STDOUT
(この場合、arbitrary_pipeline
stdin をシャッフルして大文字に変換するだけですが、名前が示すように、何でもかまいません。)
スクリプトの stdout 出力は正常に見えますが、wait
コマンドは常に失敗します。
% 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
何が間違っているのでしょうか?
ちなみに:
/usr/bin/env zsh --version
# zsh 5.0.7 (x86_64-pc-linux-gnu)
編集:
- jordanm の提案に従って、パイプラインの周囲に中括弧を追加しました
tee
。 (ただし、結果は変わりませんでした。) &!
ステファン・シャゼラス氏のコメントに応じて、 に置き換えられました&
。(この場合も、結果は変わりませんでした。)
答え1
5.0.8 より前では、zsh
すでに終了したジョブを待つことができませんでした。これは 2014 年の 5.0.8 で変更されました。変更内容をご覧ください。そこには。
/dev/null
ここでは、問題を無視するために stderr をリダイレクトするだけです:
wait $pid 2> /dev/null
以下の点に注意してください:
{ tee $tmpdir/orig | arbitrary_pipeline > $tmpdir/alt; } &
最適化のため、zsh
は追加のプロセスをフォークせずarbitrary_pipeline
、バックグラウンドで開始されたサブシェルを実行するプロセスと同じプロセスでそれを実行します。
paste
は、stdin に EOF が表示されるまで終了しません。stdin とは、パイプの$pid
反対側で書き込みを行っているパイプ (およびその子が存在する場合) のことです。したがって、$pid
パイプの書き込み側へのすべてのファイル記述子 (通常は stdout のみ) が閉じられるまで、EOF は表示されません。stdout を明示的に閉じない限り$pid
(これは非常にまれですが)、終了するときにのみ閉じられます。
つまりpaste
、ほとんどの場合、 の前に終了することはありませんが、念のため を$pid
実行することをお勧めします。wait
coproc
ここで、一時的な FIFO を回避するためにを使用できることに注意してください。
coproc arbitrary_pipeline
cat >&p | paste - /dev/fd/3 3<&p &
coproc : close
wait
( swait
も待機することに注意してくださいcoproc
)。