防止SIGINT关闭bash脚本中的子进程
我正在编写一个bash脚本,其中我编写了一个处理程序,当用户按下Control+C时(通过使用防止SIGINT关闭bash脚本中的子进程,bash,parent-child,child-process,sigint,Bash,Parent Child,Child Process,Sigint,我正在编写一个bash脚本,其中我编写了一个处理程序,当用户按下Control+C时(通过使用trap interruptHandler SIGINT)处理,但是SIGINT被发送到bash脚本和当前正在运行的子进程,关闭子进程。我怎样才能防止这种情况发生 编辑:这是剧本,不要对我的技能太过挑剔 #!/bin/bash trap "interruptHandler" SIGINT inInterrupt=false; quit=false; if [ -z ${cachedir+x} ];
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
(这是一个脚本,用于将matroska(mkv)文件编码为H.265,以防您好奇)在此执行了一个简单的测试,它提供了预期的结果:
int.sh
内容:
#!/bin/bash
trap '' SIGINT
tail -f /var/log/syslog >& /dev/null
测试:
$ ./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
$
编辑(回答问题编辑):
您可能希望对脚本中的每一个其他命令(例如(trap''SIGINT&&command)
)捕获并忽略SIGINT
,以便在调用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
输出:
$ 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
请注意,由于捕获了SIGINT
,因此它只持续了0.6秒
但当您忽略睡眠的SIGINT时:
function intHandler() {
echo "If SIGINT was caught, this will be printed AFTER sleep exits."
}
trap intHandler SIGINT
(trap '' SIGINT && sleep 5)
输出为:
$ 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
请注意,尽管脚本传递并捕获了
SIGINT
,但intHandler
仅在当前sleep
退出时返回,还请注意,当前sleep
未从父级捕获SIGINT
(持续了整整5秒)由于运行它的子shell((…)
)忽略了SIGINT
,因此该信号被发送到当前前台进程中的所有作业。所以防止信号传到孩子身上最简单的方法就是把它从前景中拿出来。只需通过执行以下操作来设置ffmpeg调用的背景:
...
ffmpeg -vsync passthrough -i ${cachedir}/${filename} -c:v libx265 -c:a copy -f matroska ${cachedir}/out/${filename} &
wait
...
请注意,这也为您提供了比尝试解析ps
的输出更可靠的子级pid,因此您可能需要执行以下操作:
ffmpeg ... &
ffmpegPID=$!
wait
看看这个:
#!/bin/bash
echo $$
trap 'echo "got C-c"' SIGINT
#bash -c 'trap - SIGINT; echo $$; exec sleep 60' &
sleep 60 &
pid=$!
echo "$$: waiting on $pid"
while kill -0 $pid 2>/dev/null; do
wait $pid
done
echo done
说明:
- 子
(ffmpeg
此处)必须忽略SIGINT本身。为此,请从sleep
开始,重置处理程序,然后执行bash-c
。这足以让孩子离开前景,以防止它接收到SIGINTexec
- 在父级中,简单的
将不起作用,原因如下所述。(试一试。)在这种情况下,父级将在执行其SIGINT处理程序之后但在子级完成之前继续。相反,我们使用循环并使用子pid等待等待
- 合法退出子级后,将在不存在的pid上执行另一个
,我们将忽略其stderrkill
(trap''SIGINT&&rm${cachedir}/*.mkv)
。另外,繁琐的If[-e${cachedir}
序列可以简单地用mkdir-p“$cachedir/out”替换。如果所有目录都存在,它什么也不做;如果缺少一个或多个路径组件,则会创建所有路径组件。还要注意大括号是如何完全多余的,但是双引号对于正确处理包含空格或许多其他有问题的字符的目录名是必要的。下面是另一个类似的问题:这允许运行处理程序并冻结进程,但似乎在处理程序完成后,SIGINT仍然可以传递到ffmpeg。