私はこのようなスクリプト(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
バリアントです。
それで、あなたは本当に割り込みを望まないので、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でテストすることはできませんが、ターミナルのセットアップは標準だと思います。)
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
Linuxでは、コマンドsetsid
を使用して、別のセッションでrsyncを実行できます。システムコールは存在しますが、OpenBSDはこれをコマンドとして持っていないようです。コマンドの独自のCバージョンを作成するか、Perlのこのビットをお持ちの場合は試してみてください。
#!/usr/bin/Perl
use POSIX qw(setsid);
POSIX::setsid();
exec @ARGV;
このスクリプトにmysetsid
という名前を付けると、su
コマンドのプレフィックスとして使用します(例:./mysetsid su cvsuser ...
スクリプトの最初のバージョン。割り込みは、現在のrsyncコマンドが終了するとハンドラー関数によってのみ表示されることに注意してください。
実験から、rsync
はping
などの他のツールのように動作し、呼び出し元の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
で保護します。