防止 SIGINT 在 bash 脚本中关闭子进程

Joh*_*gen 5 bash parent-child sigint child-process

我正在编写一个 bash 脚本,其中编写了一个处理程序来处理用户按下 Control+C(通过使用trap interruptHandler SIGINT)时的情况,但 SIGINT 会发送到 bash 脚本和当前正在运行的子进程,从而关闭子进程过程。我怎样才能防止这种情况发生?

编辑:这是剧本,不要过多批评我的技能..

#!/bin/bash
trap "interruptHandler" SIGINT

inInterrupt=false;
quit=false;

if [ -z ${cachedir+x} ]; then cachedir=~/.cache/zlima12.encoding; fi
cachedir=$(realpath ${cachedir});


if [ ! -e ${cachedir} ]; then mkdir ${cachedir}; fi
if [ ! -e ${cachedir}/out ]; then mkdir ${cachedir}/out; fi

cleanCache ()
{
    rm ${cachedir}/*.mkv;
    rm ${cachedir}/out/*.mkv;
}

interruptHandler ()
{
    if [ ${inInterrupt} != true ]; then
        printf "BASHPID: ${BASHPID}";
        inInterrupt=true;
        ffmpegPID=$(pgrep -P ${BASHPID});
        kill -s SIGTSTP ${ffmpegPID};
        printf "\nWould you like to quit now(1) or allow the current file to be encoded(2)? ";
        read response;
        if [ ${response} = "1" ]; then kill ${ffmpegPID}; cleanCache;
        elif [ ${response} = "2" ]; then quit=true; kill -s SIGCONT ${ffmpegPID};
        else printf "I'm not sure what you said... continuing execution.\n"; kill -s SIGCONT ${ffmpegPID};
        fi

        inInterrupt=false;
    fi
}



for param in "$@"; do

    dir=$(realpath ${param});

    if [ ! -e ${dir} ]; then
        printf "Directory ${dir} doesn't seem to exist... Exiting...\n"
        exit 1;
    elif [ -e ${dir}/new ]; then
        printf "${dir}/new already exists! Proceed? (y/n) ";
        read response;
        if [ ${response} != y ]; then exit 1; fi
    else
        mkdir ${dir}/new;
    fi

    for file in ${dir}/*.mkv; do
        filename="$(basename ${file})";
        cp $file ${cachedir}/${filename};
        ffmpeg -vsync passthrough -i ${cachedir}/${filename} -c:v libx265 -c:a copy -f matroska ${cachedir}/out/${filename};
        rm ${cachedir}/${filename};
        mv ${cachedir}/out/${filename} ${dir}/new/${filename};

        if [ ${quit} = true ]; then exit 0; fi
    done
done
Run Code Online (Sandbox Code Playgroud)

(如果您好奇的话,这是一个将 matroska (mkv) 文件编码为 H.265 的脚本)

pah*_*pah 2

在这里进行了一个简单的测试,它提供了预期的结果:

int.sh内容:

#!/bin/bash

trap '' SIGINT
tail -f /var/log/syslog >& /dev/null
Run Code Online (Sandbox Code Playgroud)

测试:

$ ./int.sh
^C^C
# ... SIGINT ignored (CTRL+C) ...
# ... Will send SIGTSTP with CTRL+Z ...
^Z
[1]+  Stopped                 ./int.sh
$ kill %1
$
[1]+  Terminated              ./int.sh
$
Run Code Online (Sandbox Code Playgroud)

编辑(回答问题编辑):

您可能希望捕获并忽略SIGINT每个其他命令(例如(trap '' SIGINT && command)在脚本中),这样您就可以防止在调用之前从当前命令捕获信号interruptHandler

正在发生的事情的一个简单示例:

#!/bin/bash

function intHandler() {
        echo "If SIGINT was caught, this will be printed AFTER sleep exits."
}

trap intHandler SIGINT

sleep 5 # Sleep will exit as soon as SIGINT is caught
Run Code Online (Sandbox Code Playgroud)

输出:

$ time ./int.sh 
^C
# ... Right here, only 0.6 seconds have elapsed before the below message being printed ...
If SIGINT was caught, this will be printed AFTER sleep exits.

real    0m0.634s
user    0m0.004s
sys     0m0.000s
Run Code Online (Sandbox Code Playgroud)

注意,由于SIGINT被抓住,只持续了0.6秒。

但是当你忽略 SIGINT 时sleep

function intHandler() {
        echo "If SIGINT was caught, this will be printed AFTER sleep exits."
}

trap intHandler SIGINT

(trap '' SIGINT && sleep 5)
Run Code Online (Sandbox Code Playgroud)

输出是:

$ time ./int.sh
^C
# ... Right here, 5 seconds have elapsed without any message ...
If SIGINT was caught, this will be printed AFTER sleep exits.

real    0m5.007s
user    0m0.000s
sys     0m0.000s
Run Code Online (Sandbox Code Playgroud)

请注意,尽管SIGINT已被脚本传递并捕获,但intHandler只有当当前sleep退出时才会返回,还要注意当前sleep并没有SIGINT从父级(它持续了整整 5 秒)作为其运行的子 shell 捕获 。 on (the ( ... )) 被忽略SIGINT