分层流程执行中处理 SIGINT 失败

分层流程执行中处理 SIGINT 失败

我有以下流程执行结构:

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
$ 

不必要的信号处理只会使事情变得复杂,并且需要更多的复杂性来尝试解决由此产生的复杂性。为什么不简化并依赖默认行为?

相关内容