次のスクリプトには、この問題を説明する以外の目的はありません。
#!/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
Schematicに渡します。
STDIN --- > --.- arbitrary_pipeline -.
\ \
paste `----<FIRST-ARGUMENT> `- <SECOND-ARGUMENT> --> STDOUT
(この場合、arbitrary_pipeline
標準入力を混ぜて大文字に変換しますが、名前が示すように何でも可能です。)
スクリプトの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)
編集する:
tee
Jordanの提案に基づいて、パイプの周りに支柱が追加されました。 (しかし結果は変わりませんでした。)- Stéphane Chazelasのコメントに従って
&!
置き換えられました。&
(今回も結果は変わりません。)
ベストアンサー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
$pid
もう一方の端でパイプ(およびその子)が書き込まれるstdinでEOFを見るまで完了しません。したがって$pid
、(および子)パイプの書き込みの終わりまで、すべてのファイル記述子(通常はstdout)を閉じるまでeofを見ることはできません。これは$pid
、標準出力が明示的に閉じられない限り、シャットダウン時にのみ発生します(非常にまれです)。
これは、ほとんどの場合、paste
その前に終了しないことが$pid
まだ良い考えであることを意味します。wait
coproc
ここでは、次を使用して一時的なFIFOを回避できます。
coproc arbitrary_pipeline
cat >&p | paste - /dev/fd/3 3<&p &
coproc : close
wait
(wait
まだsを待たなければならないことに注意してくださいcoproc
)。