10

语境:

我有一个 bash 脚本,其中包含一个子 shell 和一个用于 EXIT 伪信号的陷阱,并且它在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对主逻辑的每一行都进行记录。每当 subshel​​l 结束,或脚本因任何原因停止(EXIT 伪信号应捕获所有这些情况)时,陷阱将拦截它并运行该cleanup()函数,然后移除陷阱。rsyncand命令(sleepsleep 只是一个例子)被运行exec以防止创建僵尸进程,如果我在它们运行时杀死父脚本,并且每个可能长时间运行的命令都包装在它自己的子 shell 中,这样当exec完成,它不会终止整个脚本。

问题:

如果我在执行 exec/subshel​​l 包装命令期间中断脚本(通过kill或 CTRL+C)sleep,陷阱会正常工作,我会看到“正在清理!” 回显并记录。如果我在命令期间中断脚本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。

4

5 回答 5

3

如何将点 X 的所有输出重定向到 tee 而不必到处重复它并弄乱所有子外壳和执行程序......(希望我没有错过任何东西)

#!/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
于 2012-06-04T02:14:52.877 回答
2

除此之外set -e,我想你想要set -E

如果设置,ERR 上的任何陷阱都将由 shell 函数、命令替换和在子 shell 环境中执行的命令继承。在这种情况下,ERR 陷阱通常不会被继承。

或者,不要将命令包装在子 shell 中,而是使用花括号,它仍然可以重定向命令输出,但会在当前 shell 中执行它们。

于 2012-06-04T01:21:58.120 回答
1

如果将 INT 添加到陷阱,将正确捕获中断

trap '{
    (cleanup;) | 2>&1 tee -a $logfile
}' EXIT INT

Bash 正确地捕获了中断。但是,这并没有回答问题,为什么脚本在退出时会陷入陷阱,如果sleep被中断,也不是为什么它不会触发rsync,而是使脚本按预期工作。希望这可以帮助。

于 2012-03-16T15:27:51.763 回答
0

您的 shell 可能配置为在出错时退出:

bash # enter subshell
set -e
trap "echo woah" EXIT
sleep 4

如果您中断sleep(^C),则子 shell 将退出set -e并在此过程中打印woah

另外,有点不相关:你trap - EXIT在一个子shell中(明确地),所以在清理函数返回后它不会有效果

于 2012-03-16T15:55:36.093 回答
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

这个怎么运作

这里的技巧是我们告诉 Bash viaset -m禁用其中任何后台作业的作业控制。然后我们在后台rsync运行一个wait命令,该命令将等待最后一个运行命令rsync,直到它完成。

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

参考

于 2018-07-10T01:53:11.190 回答