通常、ジョブがバックグラウンドで開始されると、ジョブの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
で使用すると、常にその行が印刷されていることがわかります。予想通り、一度ではなく永遠にそうだった。[[ ]]
jobs
Done ...
見知らぬ人でも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
$
質問に答えていたところ、こういう言葉が出ました。質問スーパーユーザーについては、そのループを実行するより良い方法を提案する答えを投稿しないでください。私自身もいくつか考えることができます。私が質問したのは
建設
jobs
過程でなぜ違う行動をするのですか[[ ]]
?常にそのDone...
行を返しますが、手動で実行するときは返さないのはなぜですか?jobs
ループ内で実行するとスクリプトの動作が変わるのはなぜですか?
答え1
もちろん、これ$(…)
は角かっこ内のコマンドがサブシェルで実行されることを知っています。もちろん、これがjobs
シェル組み込み関数であることを知っています。まあ、jobs
シェルが死ぬと、メモリから作業が消えているようです。報告されました。ただし、実行すると$(jobs)
コマンドはサブシェルで実行されるため、親シェル(スクリプトを実行するシェル)にバックグラウンドジョブ(例では)が終了したことをjobs
通知する機会はありません。ping
報告されました。したがって、シェルがthingieを実行するためにサブシェルを作成するたびに、そのサブシェルには$(jobs)
まだ完全なタスクリストがあります(つまり、ping
タスクは5回目の反復後に終了してもそこにあります)jobs
。ping
ジョブの状態を報告します(過去4秒間停止した場合も同様)。
これはjobs
、ループで純粋なコマンドを実行すると期待どおりに終了する理由を説明します。jobs
親シェルから、親シェルはジョブの終了について知っています。報告されましたユーザーに。
インタラクティブシェルでなぜ違うのですか?これは、対話型シェルのフォアグラウンドサブプロセスが終了するたびに、シェルはフォアグラウンドプロセスの実行中に終了したすべてのバックグラウンドジョブを報告するためです1 。したがって、実行ping
中に終了しsleep 1
、sleep
終了すると、シェルはバックグラウンドジョブが終了したことを報告します。お待ちください。
1より正確な説明は、「フォアグラウンドプロセスの実行中に状態が変更されたバックグラウンドジョブ」です。一時停止されたタスク(kill -SUSP
、Ctrlプログラムで+に相当Z)、または一時停止されていないタスク(このコマンドが実行するタスク)kill -CONT
を報告することもできます。fg