Bash が rsync/subshel​​l exec ステートメント中に割り込みをトラップしない

StackOverflow https://stackoverflow.com//questions/9624947

質問

コンテクスト:

サブシェルと EXIT 疑似信号のトラップを含む bash スクリプトがありますが、実行中に割り込みを適切にトラップしません。 rsync. 。以下に例を示します。

#!/bin/bash
logfile=/path/to/file;
directory1=/path/to/dir
directory2=/path/to/dir

cleanup () {
     echo "Cleaning up!"
     #do stuff
     trap - EXIT 
}

trap '{
    (cleanup;) | 2>&1 tee -a $logfile
}' EXIT

(
    #main script logic, including the following lines:
    (exec sleep 10;);        
    (exec rsync --progress -av --delete $directory1 /var/tmp/$directory2;);

)  | 2>&1 tee -a $logfile
trap - EXIT #just in case cleanup isn't called for some reason

スクリプトのアイデアは次のとおりです。重要なロジックのほとんどは、パイプ経由で接続されるサブシェル内で実行されます。 tee そしてログファイルに保存されるので、そうする必要はありません tee メインロジックのすべての行をすべてログに記録します。サブシェルが終了するか、何らかの理由でスクリプトが停止されると (EXIT 疑似信号はこれらすべてのケースをキャプチャする必要があります)、トラップはそれをインターセプトし、 cleanup() 機能を有効にしてトラップを削除します。の rsync そして sleep コマンド (スリープは単なる例です) が実行されます exec 実行中に親スクリプトを強制終了した場合、ゾンビ プロセスの作成を防ぐため、実行時間が長い可能性のある各コマンドは独自のサブシェルでラップされるため、 exec 終了しても、スクリプト全体は終了しません。

問題:

スクリプトを中断した場合 ( kill または CTRL+C) 実行/サブシェルのラップ中 sleep コマンド、トラップは適切に機能し、「クリーンアップ!」が表示されます。エコーとログに記録されました。途中でスクリプトを中断すると、 rsync コマンド、わかりました rsync 終わって、書く rsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at rsync.c(544) [sender=3.0.6] 画面に表示されると、スクリプトは終了します。クリーンアップもトラップもありません。なぜ中断/強制終了しないのですか rsync 罠を発動させる?

使ってみました --no-detach rsyncで切り替えましたが、何も変わりませんでした。私はbash 4.1.2、rsync 3.0.6、centOS 6.2を持っています。

役に立ちましたか?

解決

どこでも繰り返したり、すべてのサブシェルや実行プログラムをいじったりせずに、ポイント X からのすべての出力を tee にリダイレクトするだけにしてはどうでしょうか...(何か見逃してなかったらいいのですが)

#!/bin/bash
logfile=/path/to/file;
directory1=/path/to/dir
directory2=/path/to/dir

exec > >(exec tee -a $logfile) 2>&1

cleanup () {
     echo "Cleaning up!"
     #do stuff
     trap - EXIT 
}
trap cleanup EXIT

sleep 10
rsync --progress -av --delete $directory1 /var/tmp/$directory2

他のヒント

に加えて set -e, 、欲しいと思います set -E:

設定すると、ERR のトラップはシェル関数、コマンド置換、およびサブシェル環境で実行されるコマンドによって継承されます。このような場合、通常、ERR トラップは継承されません。

あるいは、コマンドをサブシェルでラップする代わりに、中括弧を使用すると、コマンド出力をリダイレクトできますが、コマンド出力は現在のシェルで実行されます。

トラップにINTを追加すると割り込みが適切にキャッチされます

trap '{
    (cleanup;) | 2>&1 tee -a $logfile
}' EXIT INT

Bash は割り込みを正しくトラップしています。ただし、これでは、なぜスクリプトが終了時にトラップするのかという質問には答えられません。 sleep 中断されるか、なぜトリガーされないのか rsync, ですが、スクリプトは想定どおりに動作します。お役に立てれば。

シェルはエラー時に終了するように設定されている可能性があります。

bash # enter subshell
set -e
trap "echo woah" EXIT
sleep 4

中断したら sleep (^C) その後、サブシェルは次の理由で終了します。 set -e そして印刷する woah 過程の中で。

また、少し関係ありませんが、あなたの trap - EXIT (明示的に) サブシェル内にあるため、クリーンアップ関数が戻った後は効果がありません。

実験から明らかなことは、 rsync などの他のツールと同様に動作します ping また、呼び出し元の Bash 親からシグナルを継承しません。

したがって、これを少し創造的にして、次のようなことを行う必要があります。

$ cat rsync.bash
#!/bin/sh

 set -m
 trap '' SIGINT SIGTERM EXIT
 rsync -avz LargeTestFile.500M root@host.mydom.com:/tmp/. &
 wait

 echo FIN

今それを実行すると:

$ ./rsync.bash
X11 forwarding request failed
building file list ... done
LargeTestFile.500M
^C^C^C^C^C^C^C^C^C^C
sent 509984 bytes  received 42 bytes  92732.00 bytes/sec
total size is 524288000  speedup is 1027.96
FIN

ファイルが完全に転送されたことがわかります。

$ ll -h | grep Large
-rw-------. 1  501 games 500M Jul  9 21:44 LargeTestFile.500M

使い方

ここでのコツは、Bash に次のように伝えていることです。 set -m その中のバックグラウンド ジョブに対するジョブ コントロールを無効にします。次に、バックグラウンドで rsync そして、 wait 最後の実行コマンドを待機するコマンド、 rsync, 、完成するまで。

次に、スクリプト全体を次のように保護します。 trap '' SIGINT SIGTERM EXIT.

参考文献

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top