待機コマンドの失敗

待機コマンドの失敗

以下のスクリプトは、この質問を説明すること以外の目的はありません。

#!/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_pipelinestdin をシャッフルして大文字に変換するだけですが、名前が示すように、何でもかまいません。)

スクリプトの 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)

編集:

  1. jordanm の提案に従って、パイプラインの周囲に中括弧を追加しましたtee。 (ただし、結果は変わりませんでした。)
  2. &!ステファン・シャゼラス氏のコメントに応じて、 に置き換えられました&。(この場合も、結果は変わりませんでした。)

答え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)。

関連情報