语境:

我有一个 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 中运行 tee 并记录到日志文件中,所以我不必 tee 主逻辑的每一行都将其全部记录下来。每当子 shell 结束或脚本因任何原因停止时(EXIT 伪信号应该捕获所有这些情况),陷阱将拦截它并运行 cleanup() 功能,然后移除陷阱。这 rsyncsleep 命令(睡眠只是一个例子)运行 exec 如果我在运行时杀死父脚本,则可以防止创建僵尸进程,并且每个可能长时间运行的命令都包含在其自己的子 shell 中,以便当 exec 完成后,它不会终止整个脚本。

问题:

如果我中断脚本(通过 kill 或 CTRL+C) 在 exec/subshel​​l 包装期间 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。

有帮助吗?

解决方案

如何将 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

其他提示

此外 set -e, ,我想你想要 set -E:

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

或者,不要使用大括号将命令包装在子 shell 中,这仍然使您能够重定向命令输出,但会在当前 shell 中执行它们。

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

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

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

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

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

如果你打断 sleep (^C) 那么子 shell 将退出 set -e 并打印 woah 正在进行中。

另外,稍微无关:你的 trap - EXIT 位于子 shell 中(显式),因此在 cleanup 函数返回后不会产生任何效果

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

然后我们用以下方法保护整个脚本 trap '' SIGINT SIGTERM EXIT.

参考

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top