/bin/sh
私は最初にいくつかのCVSリポジトリのローカルコピーをに更新し、次に私のコンピュータからチェックrsync
アウトされたバージョンを更新するこのようなスクリプト(OpenBSDに書かれています)を持っています。このスクリプトは OpenBSD 経由で root として実行されます。doas
(sudo
OpenBSDの「同等」):
#!/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番目の部分(ストアで更新された内容によっては常に実行したくない場合があります)をスキップするためにキーを押すことができるということです。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
次のことができるはずです。無視する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
実験は、このツールが他のツール(ping
dodxなど)のように機能し、呼び出す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
実行します。wait
rsync
それからtrap '' SIGINT SIGTERM EXIT
。