(bash)スクリプトA、スクリプトBを待機しますが、その子プロセスは待機しません


9

だから私はscriptAを持っています:

ssh server1 -- scriptB &
ssh server2 -- scriptB &
ssh server3 -- scriptB &
wait
otherstuffhappens

ScriptBは次のことを行います。

rsync -av /important/stuff/. remoteserver:/remote/dir/.
rsync -av /not/so/important/stuff/. remoteserver:/remote/dir/. &
exit

私が望む結果は、scriptAは、scriptBのすべてのインスタンスが完了するのを待ってから次に進むことですが、現在はそうですが、それほど重要ではないもののバックグラウンドrsyncも待機しています。これらは、待ちたくない大きなファイルです。

nohup、disown、&の違いを読み、さまざまな組み合わせを試しましたが、探している結果が得られません。

この時点で私はかなり困惑しています。何か助けていただければ幸いです!

回答:


15

ここでの問題はsshd、コマンドのstdout(何らかの理由で、少なくとも私がテストしているバージョンではstderrのものではない)を読み取っているパイプでファイルの終わりを待機することです。そして、バックグラウンドジョブはそのパイプにfdを継承します。

そのため、これを回避するには、バックグラウンドrsyncコマンドの出力をいくつかのファイルにリダイレクトします/dev/null。sshdが対応するパイプを待機していない場合でも、終了後sshdにパイプが壊れるためrsync、stderrに書き込もうとすると強制終了されるため、stderrもリダイレクトする必要があります。

そう:

rsync ... > /dev/null 2>&1 &

比較:

$ time ssh localhost 'sleep 2 &'
ssh localhost 'sleep 2 &'  0.05s user 0.00s system 2% cpu 2.365 total
$ time ssh localhost 'sleep 2 > /dev/null &'
ssh localhost 'sleep 2 > /dev/null &'  0.04s user 0.00s system 12% cpu 0.349 total

そして:

$ ssh localhost '(sleep 1; ls /x; echo "$?" > out) > /dev/null &'; sleep 2; cat out
141  # ls by killed with SIGPIPE upon writing the error message
$ ssh localhost '(sleep 1; ls /x; echo "$?" > out) > /dev/null 2>&1 &'; sleep 2; cat out
2    # ls exited normally after writing the error on /dev/null instead
     # of a broken pipe

4

両方の親であるスクリプトを作成すると、両方を簡単に制御できます。または、2つのチャネル間に通信チャネルを確立します

特定のバックグラウンドジョブを明確に無視するに$!は、PID(最後のバックグラウンドジョブPIDです)をキャプチャして、wait $pidそのジョブが完了するまで待つだけです。


3

問題-fssh修正するためのフラグ。テスト済み:

#!/bin/sh -e
echo $$_script__begin
( echo sleeping; sleep 2; echo slept )&
echo $$_script__end

で実行するとssh localhost ./script、がslept表示されるまで待機します。-fフラグ、それはで終了echo $$_script__endし、slept以降の後にバックグラウンドで現れるsshコマンドが戻ってきました。


2

これはOpenSSHサーバーの既知の問題であり、アップストリームのBugzilla#2071で説明および説明されています。このバグでは、OpenSSH側だけでなくスクリプトにもいくつかの回避策が提案されています。

スクリプトの出力を待つ場合は、waitbefore exitも追加する必要がありscriptBます。

出力を気にしない場合は、のバリエーションとnohupへのIOリダイレクトを使用/dev/nullすると、同じ方法で問題が解決します。


1

これを試すことができます。$!最後に実行されたバックグラウンドパイプライン/プロセスのプロセスIDを含むデフォルトのシェル変数です。

command1 &
lpid1=$!

command2 &
lpid2=$!

command3 &
lpid=$!

wait $lpid1  # waits for only the process with PID lpid1  to complete. 

export変数などを使用してスクリプトごとにこれを利用する必要があります


1

Bは、それ自体のバックグラウンドプロセスを待つだけです。

rsync -av /important/stuff/. remoteserver:/remote/dir/.
rsync -av /not/so/important/stuff/. remoteserver:/remote/dir/. &
wait
exit

その時点で、2番目のrsyncをバックグラウンドで実行せず、wait完全に使用しないようにすることもできます。私は何をするものでOPの両方が実行されたものを推測しているもののrsync、それらをバックグラウンド化を意味して並列にプロセス、両方の(と&)してから使用してwait。いずれにしても、これが問題を修正する最も簡単な方法であり、質問の情報に基づいて選択する方法であることに同意します。
David Z
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.