延迟终止脚本

延迟终止脚本

我有一个像这样的脚本(/bin/sh在 OpenBSD 上编写),它首先使用 更新一些 CVS 存储库的本地副本rsync,然后更新我的计算机上这些的签出版本。该脚本通过 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,以便稍后跳过脚本的第二部分(我可能并不总是想运行,具体取决于存储库中更新的内容),而不中断循环及其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,一个人应该能够忽略信号INT,这将使子进程也忽略该信号。这并不理想,但我很乐意尝试。

因此,鉴于此,第二次尝试:

#!/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重置信号掩码的原因?第三次尝试:

#!/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。这/bin/shpdksh在 POSIX 模式下运行的 OpenBSD 的本机变体。

答案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 上安装处理程序。因此,如果您不想^C停止 rsync(我自己也讨厌这样,因为我讨厌紧急按钮不起作用),则必须确保 SIGINT 不会传递到rsync.

因此,要么告诉终端驱动程序不要向前台进程组发送 SIGINT,^C@ikkachu 的回答,或将运行该命令的进程rsync置于前台进程组之外。

与与信号处理相关的所有内容一样,其是否有效因 shell 不同(有时还因版本不同而异)。

您最好的选择是坚持使用您知道有效的已知实现,并希望几年后它仍然可以在新版本中工作。就像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

bash 实现

从实验中可以清楚地看出,它的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 viaset -m禁用其中任何后台作业的作业控制。然后,我们将后台rsync运行一个wait命令,该命令将等待最后一个运行命令,rsync直到完成。

然后我们用trap '' SIGINT SIGTERM EXIT.

参考

相关内容