![分层流程执行中处理 SIGINT 失败](https://linux22.com/image/211175/%E5%88%86%E5%B1%82%E6%B5%81%E7%A8%8B%E6%89%A7%E8%A1%8C%E4%B8%AD%E5%A4%84%E7%90%86%20SIGINT%20%E5%A4%B1%E8%B4%A5.png)
我有以下流程执行结构:
script0.sh
script1.sh
script2.sh
script3.sh
意义:
- script0.sh 执行 script1.sh。
- script1.sh 执行 script2.sh 和 script3.sh。
理想情况下,我希望在 script1.sh 中有一个陷阱处理程序,它将执行清理并退出。
由于某种原因,它不起作用。
我还尝试在所有脚本中使用陷阱处理程序,但仅调用 script0.sh 中的陷阱处理程序。
我尝试使用 script0.sh 中的陷阱处理程序来终止子进程(即 script1.sh),但该进程会挂起直到完成。
我使用命令“trap -p”并注意到只有 script0.sh 有陷阱处理程序,尽管我为所有脚本注册了陷阱处理程序。
笔记:
- script2.sh 实际上是“rsync”,其余脚本是我自己的。
- script1.sh、script2.sh 和 script3.sh 使用“&”运算符发送到后台。
- 我对发送到后台的每个进程执行“等待”。
我创建了重现此问题的脚本文件:
脚本0.sh:
#!/usr/bin/env bash
function cleanup_script0()
{
echo "SIGINT in script0.sh"
kill -s SIGINT $script1_pid
wait $script1_pid
}
trap cleanup_script0 SIGINT
./script1.sh &
script1_pid=$!
wait $script1_pid
脚本1.sh:
#!/usr/bin/env bash
function cleanup_script1()
{
echo "SIGINT in script1.sh"
kill -s SIGINT $script2_pid
wait $script2_pid
kill -s SIGINT $script3_pid
wait $script3_pid
}
trap cleanup_script1 SIGINT
rsync_file_to_copy="$( mktemp )"
dd if=/dev/urandom of="$rsync_file_to_copy" bs=1M count=1
rsync --timeout=5 --bwlimit=200 "$rsync_file_to_copy" "$( mktemp -d )/" &
script2_pid=$!
wait $script2_pid
echo "Finished script2.sh"
./script3.sh
script3_pid=$!
wait $script3_pid
echo "Finished script3.sh"
脚本3.sh:
#!/usr/bin/env bash
function cleanup_script3()
{
echo "SIGINT in script3.sh"
}
trap cleanup_script3 SIGINT
delay_counter=0
while [ 8 -gt $delay_counter ]; do
echo "script3.sh: $( date "+%H_%M_%S" )"
let 'delay_counter++'
sleep 1
done
上述脚本的执行输出如下:
rsync 期间按 CTRL+C 时的输出:
$ ./script0.sh
1+0 records in
1+0 records out
1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.00604244 s, 174 MB/s
^CSIGINT in script0.sh
rsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at rsync.c(703) [sender=3.2.3]
rsync error: received SIGINT, SIGTERM, or SIGHUP (code 20) at io.c(503) [generator=3.2.3]
Finished script2.sh
script3.sh: 23_57_24
script3.sh: 23_57_25
script3.sh: 23_57_26
script3.sh: 23_57_27
script3.sh: 23_57_28
script3.sh: 23_57_29
script3.sh: 23_57_30
script3.sh: 23_57_31
Finished script3.sh
在 script3.sh 期间按 CTRL+C 时的输出:
$ ./script0.sh
1+0 records in
1+0 records out
1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.00585018 s, 179 MB/s
Finished script2.sh
script3.sh: 23_57_46
script3.sh: 23_57_47
^CSIGINT in script0.sh
script3.sh: 23_57_48
script3.sh: 23_57_49
script3.sh: 23_57_50
script3.sh: 23_57_51
script3.sh: 23_57_52
script3.sh: 23_57_53
Finished script3.sh
答案1
A control+c将导致 SIGINT 发送到前台进程组的每个成员(或者根本没有信号;请参阅termios
手册页)。因此,我们可以通过依赖发送给进程组的信号的通常行为来简化信号处理,即使脚本实际执行的操作越来越多。
#!/bin/sh
# script0 - apparently this script does more things that were absent
# in the question, so we run the first script instead of executing
# the next script (this changes nothing from the prior result)
./script1
echo and we do some other things here ...
和script1
,现在显然在某个地方正在进行某种输出重定向,所以让我们猜测这发生在顶部(这与之前的结果没有任何变化):
#!/usr/bin/env bash
# script1 - replace rsync with a hopefully similar `sleep` call,
# plus now the output redirect, minus anything they haven't yet
# told us about
exec > >(tee and-a-ruthless-devotion-to-the-pope) 2>&1
echo RUN script2
# `command & wait;` is just a complicated and verbose way to write
# `command;` (unless there is yet another thing going on they
# haven't yet told us about??)
sleep 9
echo RUN script3
./script3
和script3
#!/usr/bin/env bash
delay_counter=0
while [[ 8 -gt $delay_counter ]]; do
echo -n "script3 time "
date +%H:%M:%S
(( delay_counter++ ))
sleep 1
done
之后chmod +x script*
仍然再次导致
$ ./script0
RUN script2
^C
$ ./script0
RUN script2
RUN script3
script3 time 16:28:31
script3 time 16:28:32
script3 time 16:28:33
^C
$
不必要的信号处理只会使事情变得复杂,并且需要更多的复杂性来尝试解决由此产生的复杂性。为什么不简化并依赖默认行为?