バックグラウンドプロセスが完了するまでループする(「jobs」を使用)ことはスクリプトでは機能しません

バックグラウンドプロセスが完了するまでループする(「jobs」を使用)ことはスクリプトでは機能しません

バックグラウンド プロセス (スクリプト内で先に起動) が完了するまでループしようとしています。簡単に再現できるテスト ケースは次のとおりです。

ping -c 10 localhost &>/dev/null &

コマンドラインでは、ループできますwhile [[ -n $(jobs) ]](while$(jobs)は null ではありません)。

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

ただし、スクリプト内の同じ 2 行は、.を押すまで sを出力し続けますCtrl-C

jobs奇妙なことに、ループ内で呼び出すと、スクリプトは期待どおりに終了します。

$ ./background-ping.sh
.[1]+  Running                 ping -c 5 localhost &> /dev/null &
.[1]+  Running                 ping -c 5 localhost &> /dev/null &
.[1]+  Running                 ping -c 5 localhost &> /dev/null &
.[1]+  Running                 ping -c 5 localhost &> /dev/null &
.[1]+  Done                    ping -c 5 localhost &> /dev/null

バックグラウンド ジョブが完了したかどうかを確認する他の方法 ( のチェックなど) があることは承知していますが、チェックが期待どおりに機能しない/proc理由を知りたいです。jobs

答え1

あなたの問題を理解しているなら、スクリプト内で 2 行が止まらないということです。

私が作ったのは次のものです:

ping -c 5 google.com &>/dev/null &
while [[ -n $(jobs -r) ]]; do echo -n "."; sleep 1; done

jobs -r実行中のプロセスをチェックし、スクリプトの呼び出しが期待どおりに機能し、ping が完了するとスクリプトが停止します。

編集: スクリプトでは親プロセスは実行中のプロセスとして知られているので、jobs実行中のプロセスがあると考え続けるべきだと思います。これは仮説です。

それが答えですか?(あるいは、私があなたの言っていることを本当に理解していないのかもしれません。私の英語が下手なのが問題かもしれません...)

答え2

このような問題が発生した場合は、常に変数を印刷して何が起こっているかを把握するようにしてください。この場合、jobs完了したジョブも返されます。これを実行すると:

ping -c 5 localhost &>/dev/null &
while [[ -n $(jobs | tee -a temp) ]]; do
    echo -n .; 
    sleep 1; 
done

次のような出力が表示されますtemp

[1]+  Running                 ping -c 5 localhost &> /dev/null &
[1]+  Running                 ping -c 5 localhost &> /dev/null &
[1]+  Running                 ping -c 5 localhost &> /dev/null &
[1]+  Running                 ping -c 5 localhost &> /dev/null &
[1]+  Done                    ping -c 5 localhost &> /dev/null
[1]+  Done                    ping -c 5 localhost &> /dev/null
[1]+  Done                    ping -c 5 localhost &> /dev/null
[1]+  Done                    ping -c 5 localhost &> /dev/null
[1]+  Done                    ping -c 5 localhost &> /dev/null
[...]

したがって、ここでの出力はjobs決して空にはなりません。ジョブが終了しても、メッセージjobsは返されますDone。これが、Metal3d のソリューションが機能jobs -rした理由です。

さらに混乱するのは、なぜループ内で実行すると正しく動作するのかということです。答えは、別のサブシェルで実行されるjobsという事実と関係があると思われますが、詳細についてはよくわかりません。while [[ -n $(jobs) ]]jobs質問を投稿しました誰かが興味を持っているなら、これについてU&Lについて。

答え3

waitバックグラウンドジョブが停止するのを待つコマンドを使用することができます

例:

$ ping -c 10 localhost &>/dev/null &
[29787]
$ wait
[1]+  Done ping -c 10 localhost &>/dev/null

wait コマンドはブロックし、ping終了するとプロンプトが解放され、メッセージが出力されます。

いくつかのコマンドで使用できます:

$ ping -c 5 localhost &>/dev/null & ping -c 5 facebook.com  &>/dev/null &
[1] 29867
[2] 29868
$ wait
[1]-  Done                    ping -c 5 localhost &>/dev/null
[2]+  Done                    ping -c 5 facebook.com &>/dev/null

関連情報