web-dev-qa-db-ja.com

スクリプトの終了を遅らせる

私はこのようなスクリプト(OpenBSDで/bin/sh用に書かれています)を持っています。これは最初にrsyncを使用していくつかのCVSリポジトリのローカルコピーを更新し、次に私のマシンでこれらのチェックアウトバージョンを更新します。スクリプトは、OpenBSDの doas (OpenBSDではSudo "equivalent")を介してrootとして実行されます。

#!/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+Crsyncループの実行中に、ループとそのsu + rsyncプロセス。

これは機能せず、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を実行しているときは、ignoreINTシグナル。これにより、子プロセスもそのシグナルを無視します。これは理想的ではありませんが、試してみたいと思います。

それで、それに照らして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プロセスは、同じ方法でstill割り込み可能です。

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

私のマシンのbashはリリース4.4.23です。 /bin/shは、POSIXモードで実行されているOpenBSDのネイティブpdkshバリアントです。

4
Kusalananda

それで、あなたは本当に割り込みを望まないので、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でテストすることはできませんが、ターミナルのセットアップは標準だと思います。)

5
ilkkachu

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

したがって、 @ ikkachuの回答 のように^CでSIGINTをフォアグラウンドプロセスグループに送信しないようにターミナルドライバに指示するか、その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コマンドが終了するとハンドラー関数によってのみ表示されることに注意してください。

2
meuh

Bashの実装

実験から、rsyncpingなどの他のツールのように動作し、呼び出し元の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

使い方

ここでの秘訣は、set -mを介してBashに、その中のバックグラウンドジョブのジョブコントロールを無効にするように指示することです。次に、rsyncをバックグラウンドで実行し、waitコマンドを実行します。このコマンドは、最後の実行コマンドrsyncが完了するまで待機します。

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

参考文献

1
slm