遅延終了スクリプト

遅延終了スクリプト

/bin/sh私は最初にいくつかのCVSリポジトリのローカルコピーをに更新し、次に私のコンピュータからチェックrsyncアウトされたバージョンを更新するこのようなスクリプト(OpenBSDに書かれています)を持っています。このスクリプトは OpenBSD 経由で root として実行されます。doassudoOpenBSDの「同等」):

#!/bin/sh

int_handler () {
    echo 'Wait...' >&2
    quit=1
}

quit=0
trap int_handler INT

for r in CVSROOT src xenocara ports; do
    su cvsuser -c 'rsync --archive --itemize-changes --delete --omit-dir-times \
        "rsync://anoncvs.eu.openbsd.org/OpenBSD-cvs/$r/" \
        "/extra/cvs/$r/"'
done

trap - INT

[ "$quit" -eq 1 ] && exit

# rest of script updates checked-out CVS repositories from local copy

アイデアは、ループの実行Ctrl+C中にループとそのプロセスをrsync中断することなく、後でスクリプトの2番目の部分(ストアで更新された内容によっては常に実行したくない場合があります)をスキップするためにキーを押すことができるということです。sursync

これは機能せず、rsyncプロセスは信号を受信して​​終了します(そしてforループの次の反復が続きます)。 rsync次のように言います(Ctrl+Cスクリプトが終了するまで数回押したとき)。

^Crsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at rsync.c(642) [generator=3.1.3]
rsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at io.c(504) [receiver=3.1.3]
Wait...
rsync: [receiver] write error: Broken pipe (32)
rsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at io.c(1633) [sender=3.1.3]
^Crsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at rsync.c(642) [generator=3.1.3]rsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at io.c(504) [receiver=3.1.3
]

rsync: [receiver] write error: Broken pipe (32)
Wait...
^Crsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at rsync.c(642) [generator=3.1.3]
rsync error: received SIGUSR1 (code 19) at main.c(1440) [receiver=3.1.3]
Wait...

「という質問に対する回答によると、ctrl+c /not/ while ループを中断するには?「少なくとも実行中は、bash次のことができるはずです。無視するsignal INT、これは子プロセスもシグナルを無視します。理想的ではありませんが、試してみることができて嬉しいです。

したがって、2番目の試みは次のようになります。

#!/usr/local/bin/bash

trap '' INT

for r in CVSROOT src xenocara ports; do
    su cvsuser -c 'rsync --archive --itemize-changes --delete --omit-dir-times \
        "rsync://anoncvs.eu.openbsd.org/OpenBSD-cvs/$r/" \
        "/extra/cvs/$r/"'
done

trap - INT

read -p 'Press enter or interrupt...' junk

# rest of script updates checked-out CVS repositories from local copy

これによりrsyncプロセスが中断される可能性がありますCtrl+C

まあ、おそらくsu信号マスクをリセットする理由ですか? 3回目の試み:

#!/usr/local/bin/bash

trap '' INT

for r in CVSROOT src xenocara ports; do
    su -s /usr/local/bin/bash cvsuser -c \
        'trap "" INT; rsync --archive --itemize-changes --delete --omit-dir-times \
        "rsync://anoncvs.eu.openbsd.org/OpenBSD-cvs/$r/" \
        "/extra/cvs/$r/"'
done

trap - INT

read -p 'Press enter or interrupt...' junk

# rest of script updates checked-out CVS repositories from local copy

いいえ、喜びはありません。プロセスrsyncは次のとおりです。まだ同様に中断可能です。

私が(最初に)意図したとおりにスクリプトを機能させる方法はありますか?

bash私のコンピュータのバージョンは4.4.23です。これはPOSIXモードで動作するOpenBSDの基本的なバリエーション/bin/shです。pdksh

答え1

それでは、実際に割り込みをしたくないのですが、ターミナルレベルで割り込みをどのように無効にしますかstty intr ""?これにより、入力バッファにプレーン文字として表示され、^Cそこから読み取ることができます。

これはLinuxで私にとって効果的です。

#!/bin/bash

t=$(stty -g)
stty intr ""
echo please hold
sleep 5
stty "$t"

# short timeout, just check if there was any input earlier
read -n1 -t0.1 a
[[ "$a" = $'\003' ]] && echo "you hit ^C"

(OpenBSDではテストできませんでしたが、端末設定は標準であると仮定します。)

答え2

rsync起動時にSIGINTが無視されても、ハンドラはSIGINTにインストールされます。したがって、rsyncを停止したくない場合^C(パニックボタンが機能しないため、自分で実行するのが嫌いです)SIGINTがrsync

したがって、ターミナルドライバにSIGINTをフォアグラウンドプロセスグループに送信しないでください^C@ikkachuの返信rsync、またはフォアグラウンドプロセスグループの外部にコマンドを実行するプロセスを配置します。

信号処理に関連するすべてと同様に、これが機能するかどうかはシェルごとに(時にはバージョンごとに)異なります。

最善の方法は、動作していることがわかっている既知の実装に固執し、数年後でも新しいバージョンでも動作することを望むことです。良いzsh

#! /usr/bin/env zsh

# enable job control (run pipelines in different process groups)
set -o monitor

interrupted() false
trap 'echo Wait...; interrupted() true' INT

# run in background so it won't get the SIGINT upon ^C
# as it won't be in the foreground process group. Note that with
# version 5.1.1 at least, running it without & will also run in
# in a new process group, and not make it the foreground process
# group of the terminal as the shell is not interactive. However
# the trap would be run asynchronously.
rsync... &

while (($#jobstates)) {wait}

interrupted && exit

echo rest of the script

答え3

Linuxでは、このコマンドを使用してsetsid別々のセッションでrsyncを実行できます。システムコールは存在しますが、OpenBSDにはそれをコマンドとして含めないようです。 Cバージョンのコマンドを直接書くか、可能であればPerlを試してみてください。

#!/usr/bin/perl
use POSIX qw(setsid);
POSIX::setsid();
exec @ARGV;

スクリプト名を指定すると、mysetsidその名前をコマンドのプレフィックスとして使用できますsu。たとえば、./mysetsid su cvsuser ...スクリプトの最初のバージョンで使用できます。ハンドラ機能は、現在のrsyncコマンドが完了した後にのみ割り込みを見ることができます。

答え4

バッシュの実装

rsync実験は、このツールが他のツール(pingdodxなど)のように機能し、呼び出すBash親ツールからシグナルを継承しないことを明らかにしました。

したがって、いくつかの創造性を発揮して次のことを行う必要があります。

$ cat rsync.bash
#!/bin/sh

 set -m
 trap '' SIGINT SIGTERM EXIT
 rsync -avz LargeTestFile.500M [email protected]:/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実行します。waitrsync

それからtrap '' SIGINT SIGTERM EXIT

引用する

関連情報