上下文
我有一个bash脚本,其中包含EXIT伪信号的子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;) | 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中,通过tee
和日志文件传送,所以我没有tee
的每一行让它全部记录的主要逻辑。每当子shell结束,或脚本因任何原因停止(EXIT伪信号应捕获所有这些情况),陷阱将拦截它并运行cleanup()
函数,然后删除陷阱。 rsync
和sleep
命令(睡眠只是一个示例)通过exec
运行,以防止在我们运行时删除父脚本时创建僵尸进程,并且每个可能长时间运行的命令包含在它自己的子shell中,这样当exec
完成时,它不会终止整个脚本。
问题:
如果我在exec / subshell wrapped kill
命令期间中断脚本(通过sleep
或CTRL + C),则陷阱正常工作,我看到“正在清理!”回应并记录。如果我在rsync
命令期间中断脚本,我看到rsync
结束,并将rsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at rsync.c(544) [sender=3.0.6]
写入屏幕,然后脚本就会死掉;没有清理,没有陷阱。为什么中断/查杀rsync
不会触发陷阱?
我尝试使用--no-detach
开关和rsync,但它没有改变任何东西。
我有bash 4.1.2,rsync 3.0.6,centOS 6.2。
答案 0 :(得分:2)
除set -e
外,我认为您需要set -E
:
如果设置,则ERR上的任何陷阱都由shell函数,命令替换和在子shell环境中执行的命令继承。在这种情况下,ERR陷阱通常不会被继承。
或者,不是将命令包装在子shell中,而是使用花括号,它仍然能够重定向命令输出,但会在当前shell中执行它们。
答案 1 :(得分:2)
如何将X点的所有输出重定向到tee,而不必在任何地方重复它并弄乱所有的子shell和执行者......(希望我没有错过任何东西)
#!/bin/bash
logfile=/path/to/file;
directory1=/path/to/dir
directory2=/path/to/dir
exec > >(exec tee -a $logfile) 2>&1
cleanup () {
echo "Cleaning up!"
#do stuff
trap - EXIT
}
trap cleanup EXIT
sleep 10
rsync --progress -av --delete $directory1 /var/tmp/$directory2
答案 2 :(得分:1)
如果将INT添加到陷阱
,将正确捕获中断trap '{
(cleanup;) | 2>&1 tee -a $logfile
}' EXIT INT
Bash正在正确捕获中断。但是,这并不能解决问题,如果sleep
被中断,脚本为什么会在退出时陷阱,也不会为什么它不会在rsync
上触发,而是让脚本按预期工作。希望这会有所帮助。
答案 3 :(得分:0)
您的shell可能配置为在出错时退出:
bash # enter subshell
set -e
trap "echo woah" EXIT
sleep 4
如果您中断sleep
(^ C),则子shell将因set -e
而退出,并在此过程中打印woah
。
另外,稍微不相关:你的trap - EXIT
在子shell中(显式),所以在清理函数返回后它不会有效
答案 4 :(得分:0)
从实验中可以很明显地看出,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
保护整个脚本。