Linux Bash在rsync/subshell exec语句期间不捕获中断
上下文: 我有一个bash脚本,它包含一个子shell和一个用于退出伪信号的陷阱,并且它在Linux Bash在rsync/subshell exec语句期间不捕获中断,linux,bash,shell,rsync,bash-trap,Linux,Bash,Shell,Rsync,Bash Trap,上下文: 我有一个bash脚本,它包含一个子shell和一个用于退出伪信号的陷阱,并且它在rsync期间没有正确地捕捉中断。下面是一个例子: #!/bin/bash logfile=/path/to/file; directory1=/path/to/dir directory2=/path/to/dir cleanup () { echo "Cleaning up!" #do stuff trap - EXIT } trap '{ (cleanup;
rsync
期间没有正确地捕捉中断。下面是一个例子:
#!/bin/bash
logfile=/path/to/file;
directory1=/path/to/dir
directory2=/path/to/dir
cleanup () {
echo "Cleaning up!"
#do stuff
trap - EXIT
}
trap '{
(cleanup;) | 2>&1 tee -a $logfile
}' EXIT
(
#main script logic, including the following lines:
(exec sleep 10;);
(exec rsync --progress -av --delete $directory1 /var/tmp/$directory2;);
) | 2>&1 tee -a $logfile
trap - EXIT #just in case cleanup isn't called for some reason
该脚本的思想是这样的:大多数重要的逻辑运行在一个子shell中,该子shell通过tee
管道传输到一个日志文件,因此我不必tee
主逻辑的每一行都将其记录下来。每当子shell结束,或脚本因任何原因停止时(退出伪信号应捕获所有这些情况),陷阱将拦截它并运行cleanup()
函数,然后移除陷阱。rsync
和sleep
命令(sleep只是一个示例)通过exec
运行,以防止在父脚本运行时杀死它们时创建僵尸进程,并且每个可能长时间运行的命令都包装在自己的子shell中,这样当exec
完成时,它不会终止整个脚本
问题:
如果我在exec/subshell wrappedsleep
命令期间中断脚本(通过kill
或CTRL+C),陷阱工作正常,我会看到“Cleaning up!”回声并记录。如果在执行rsync
命令期间中断脚本,我会看到rsync
结束,并将rsync错误:在rsync.c(544)[sender=3.0.6]
处接收到的SIGINT、SIGTERM或SIGHUP(代码20)写入屏幕,然后脚本就消失了;没有清理,没有陷阱。为什么中断/终止rsync
不会触发陷阱
我曾尝试将--no detach
开关与rsync一起使用,但它没有改变任何东西。
我有bash4.1.2、rsync3.0.6和centos6.2。如果您将INT添加到陷阱中,中断将被正确捕获
trap '{
(cleanup;) | 2>&1 tee -a $logfile
}' EXIT INT
Bash正在正确捕获中断。但是,这并不能回答这样一个问题:如果
睡眠
被中断,为什么脚本会在退出时陷入陷阱,或者为什么它不会在rsync
上触发,而是使脚本按预期工作。希望这有帮助。您的shell可能被配置为在出现错误时退出:
bash # enter subshell
set -e
trap "echo woah" EXIT
sleep 4
如果中断sleep
(^C),则子shell将由于set-e
而退出,并在此过程中打印woah
另外,有点不相关:您的
陷阱-退出
位于子shell(显式)中,因此在清理函数返回后它不会产生效果除了set-e
,我想您需要set-e
:
如果设置了ERR,则shell函数、命令替换和在子shell环境中执行的命令将继承ERR上的任何陷阱。在这种情况下,错误陷阱通常不会被继承
或者,不要将命令包装在子shell中,而是使用大括号,这样仍然可以重定向命令输出,但可以在当前shell中执行它们。将点X的所有输出重定向到tee,而不必到处重复,并将所有子shell和执行都弄乱。。。(希望我没有错过什么)
从实验中可以非常清楚地看到,
rsync
的行为与其他工具(如ping
)类似,并且不会从调用Bash父级继承信号
因此,你必须在这方面有一点创意,并采取如下措施:
$ cat rsync.bash
#!/bin/sh
set -m
trap '' SIGINT SIGTERM EXIT
rsync -avz LargeTestFile.500M root@host.mydom.com:/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
保护整个脚本
工具书类
陷阱-退出
位于子shell中(显式),因此,在清理函数returns之后,它不会产生任何效果。在子shell中运行exec与正常运行命令是一样的——您不需要额外的标点符号。这看起来不错,但我实现了它,它产生了与预期相反的效果。如果我将INT添加到陷阱调用中,sleep
语句不再触发陷阱,rsync
也不会触发陷阱。当我删除INT(不更改任何其他内容)sleep
再次触发陷阱。删除上一个“以防万一”陷阱,脚本也可以很好地处理退出陷阱。然而,陷阱中调用清理的INT应该是处理中断的正确方法。与此无关,但正如sehe所写,子shell中的陷阱没有任何作用。这实际上是我的第一个解决方案,但我们的一些环境无法进行重定向,因为有一个奇怪的Bash版本或其他什么……所以我求助于“将整个东西包装在一个块中,并将其全部指向tee”黑客。好的观点。@ZacB你能更具体地描述一下“怪异版本的bash”吗?在某些情况下(当作为/bin/sh
运行时),它运行n Posix兼容模式。。。如果是这种情况,则在执行之前添加set+o posix
,这就是@nhed的情况;通过切换到/bin/bash
解决了此问题。
$ ll -h | grep Large
-rw-------. 1 501 games 500M Jul 9 21:44 LargeTestFile.500M