Bash没有在rsync / subshel​​l exec语句中捕获中断

时间:2012-03-08 21:06:30

标签: linux bash shell rsync bash-trap

上下文

我有一个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()函数,然后删除陷阱。 rsyncsleep命令(睡眠只是一个示例)通过exec运行,以防止在我们运行时删除父脚本时创建僵尸进程,并且每个可能长时间运行的命令包含在它自己的子shell中,这样当exec完成时,它不会终止整个脚本。

问题:

如果我在exec / subshel​​l 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。

5 个答案:

答案 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保护整个脚本。

参考文献