スクリプト内のサブシェルで実行されると、「タスク」が常に完了したプロセスの行を返すのはなぜですか?

スクリプト内のサブシェルで実行されると、「タスク」が常に完了したプロセスの行を返すのはなぜですか?

通常、ジョブがバックグラウンドで開始されると、ジョブのjobs完了後に最初に実行すると完了が報告されますが、後続の実行では何も報告されません。

$ ping -c 4 localhost &>/dev/null &
[1] 9666
$ jobs
[1]+  Running                 ping -c 4 localhost &> /dev/null &
$ jobs
[1]+  Done                    ping -c 4 localhost &> /dev/null
$ jobs  ## returns nothing
$ 

しかし、スクリプト内のサブシェルで実行すると、常に値を返すようです。スクリプトは終了しません。

#!/usr/bin/env bash
ping -c 3 localhost &>/dev/null &
while [[ -n $(jobs) ]]; do
    sleep 1; 
done

出力を表示するためにコンストラクタteeで使用すると、常にその行が印刷されていることがわかります。予想通り、一度ではなく永遠にそうだった。[[ ]]jobsDone ...

見知らぬ人でもjobsループ内で実行すると、予想通りに終了します。

#!/usr/bin/env bash
ping -c 3 localhost &>/dev/null &
while [[ -n $(jobs) ]]; do
    jobs
    sleep 1; 
done

最後に、@muryが指摘したように、最初のスクリプトは期待どおりに機能し、コマンドラインから実行すると終了します。

$ ping -c 5 localhost &>/dev/null & 
[1] 13703
$ while [[ -n $(jobs) ]]; do echo -n . ; sleep 1; done
...[1]+  Done                    ping -c 5 localhost &> /dev/null
$ 

質問に答えていたところ、こういう言葉が出ました。質問スーパーユーザーについては、そのループを実行するより良い方法を提案する答えを投稿しないでください。私自身もいくつか考えることができます。私が質問したのは

  1. 建設jobs過程でなぜ違う行動をするのですか[[ ]]?常にそのDone...行を返しますが、手動で実行するときは返さないのはなぜですか?

  2. jobsループ内で実行するとスクリプトの動作が変わるのはなぜですか?

答え1

もちろん、これ$(…)は角かっこ内のコマンドがサブシェルで実行されることを知っています。もちろん、これがjobsシェル組み込み関数であることを知っています。まあ、jobsシェルが死ぬと、メモリから作業が消えているようです。報告されました。ただし、実行すると$(jobs)コマンドはサブシェルで実行されるため、親シェル(スクリプトを実行するシェル)にバックグラウンドジョブ(例では)が終了したことをjobs通知する機会はありません。ping報告されました。したがって、シェルがthingieを実行するためにサブシェルを作成するたびに、そのサブシェルには$(jobs)まだ完全なタスクリストがあります(つまり、pingタスクは5回目の反復後に終了してもそこにあります)jobspingジョブの状態を報告します(過去4秒間停止した場合も同様)。

これはjobs、ループで純粋なコマンドを実行すると期待どおりに終了する理由を説明します。jobs 親シェルから、親シェルはジョブの終了について知っています。報告されましたユーザーに。

インタラクティブシェルでなぜ違うのですか?これは、対話型シェルのフォアグラウンドサブプロセスが終了するたびに、シェルはフォアグラウンドプロセスの実行中に終了したすべてのバックグラウンドジョブを報告するためです1 したがって、実行ping中に終了しsleep 1sleep終了すると、シェルはバックグラウンドジョブが終了したことを報告します。お待ちください。


1より正確な説明は、「フォアグラウンドプロセスの実行中に状態が変更されたバックグラウンドジョブ」です。一時停止されたタスク(kill -SUSPCtrlプログラムで+に相当Z)、または一時停止されていないタスク(このコマンドが実行するタスク)kill -CONTを報告することもできます。fg

関連情報