bash 陷阱中断命令,但应在循环结束时退出
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26808727/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
bash trap interrupt command but should exit on end of loop
提问by UsersUser
I′ve asked Bash trap - exit only at the end of loopand the submitted solution works but while pressing CTRL-C the running command in the script (mp3convert with lame) will be interrupt and than the complete for loop will running to the end. Let me show you the simple script:
我问过Bash 陷阱 - 仅在循环结束时退出,提交的解决方案有效,但在按下 CTRL-C 时,脚本中的运行命令(mp3convert with lame)将被中断,然后完整的 for 循环将运行到最后. 让我向您展示一个简单的脚本:
#!/bin/bash
mp3convert () { lame -V0 file.wav file.mp3 }
PreTrap() { QUIT=1 }
CleanUp() {
if [ ! -z $QUIT ]; then
rm -f $TMPFILE1
rm -f $TMPFILE2
echo "... done!" && exit
fi }
trap PreTrap SIGINT SIGTERM SIGTSTP
trap CleanUp EXIT
case in
write)
while [ -n "$line" ]
do
mp3convert
[SOMEMOREMAGIC]
CleanUp
done
;;
QUIT=1
If I press CTRL-C while function mp3convert is running the lame command will be interrupt and then [SOMEMOREMAGIC] will execute before CleanUp is running. I don′t understand why the lame command will be interrupt and how I could avoid them.
如果我在函数 mp3convert 运行时按 CTRL-C,跛脚命令将被中断,然后 [SOMEMOREMAGIC] 将在 CleanUp 运行之前执行。我不明白为什么蹩脚的命令会被中断以及我如何避免它们。
采纳答案by Robin Hsu
Try to simplify the discussion above, I wrap up an easier understandable version of show-case script below. This script also HANDLES the "double control-C problem":
(Double control-C problem: If you hit control C twice, or three times, depending on how many wait $PID
you used, those clean up can not be done properly.)
尽量简化上面的讨论,我在下面总结了一个更容易理解的展示脚本版本。该脚本还处理了“双控制-C问题”:(双控制-C问题:如果您按控制C两次或三下,取决于wait $PID
您使用的次数,那些清理工作将无法正确完成。)
#!/bin/bash
mp3convert () {
echo "mp3convert..."; sleep 5; echo "mp3convert done..."
}
PreTrap() {
echo "in trap"
QUIT=1
echo "exiting trap..."
}
CleanUp() {
### Since 'wait $PID' can be interrupted by ^C, we need to protected it
### by the 'kill' loop ==> double/triple control-C problem.
while kill -0 $PID >& /dev/null; do wait $PID; echo "check again"; done
### This won't work (A simple wait $PID is vulnerable to double control C)
# wait $PID
if [ ! -z $QUIT ]; then
echo "clean up..."
exit
fi
}
trap PreTrap SIGINT SIGTERM SIGTSTP
#trap CleanUp EXIT
for loop in 1 2 3; do
(
echo "loop #$loop"
mp3convert
echo magic 1
echo magic 2
echo magic 3
) &
PID=$!
CleanUp
echo "done loop #$loop"
done
The kill -0
trick can be found in a comment of this link
kill -0
可以在此链接的评论中找到该技巧
回答by mattias
One way of doing this would be to simply disable the interrupt until your program is done. Some pseudo code follows:
一种方法是简单地禁用中断,直到您的程序完成。部分伪代码如下:
#!/bin/bash
# First, store your stty settings and disable the interrupt
STTY=$(stty -g)
stty intr undef
#run your program here
runMp3Convert()
#restore stty settings
stty ${STTY}
# eof
Another idea would be to run your bash script in the background (if possible).
另一个想法是在后台运行您的 bash 脚本(如果可能)。
mp3convert.sh &
or even,
甚至,
nohup mp3convert.sh &
回答by PM 2Ring
When you hit Ctrl-C in a terminal, SIGINT gets sent to all processes in the foreground process group of that terminal, as described in this Stack Exchange "Unix & Linux" answer: How Ctrl C works. (The other answers in that thread are well worth reading, too). And that's why your mp3convert function gets interrupted even though you have set a SIGINT trap.
当您在终端中按下 Ctrl-C 时,SIGINT 将被发送到该终端的前台进程组中的所有进程,如 Stack Exchange“Unix & Linux”答案中所述:Ctrl C 的工作原理。(该线程中的其他答案也非常值得一读)。这就是为什么即使您设置了 SIGINT 陷阱,您的 mp3convert 函数也会被中断的原因。
But you can get around that by running the mp3convert function in the background, as mattias mentioned. Here's a variation of your script that demonstrates the technique.
但是你可以通过在后台运行 mp3convert 函数来解决这个问题,正如马蒂亚斯所提到的。这是演示该技术的脚本的变体。
#!/usr/bin/env bash
myfunc()
{
echo -n "Starting :"
for i in {1..7}
do
echo -n " $i"
sleep 1
done
echo ". Finished "
}
PreTrap() { QUIT=1; echo -n " in trap "; }
CleanUp() {
#Don't start cleanup until current run of myfunc is completed.
wait $pid
[[ -n $QUIT ]] &&
{
QUIT=''
echo "Cleaning up"
sleep 1
echo "... done!" && exit
}
}
trap PreTrap SIGINT SIGTERM SIGTSTP
trap CleanUp EXIT
for i in {a..e}
do
#Run myfunc in background but wait until it completes.
myfunc "$i" &
pid=$!
wait $pid
CleanUp
done
QUIT=1
When you hit Ctrl-C while myfunc
is in the middle of a run, PreTrap
prints its message and sets the QUIT flag, but myfunc
continues running and CleanUp
doesn't commence until the current myfunc
run has finished.
当您在运行过程中按 Ctrl-C 时myfunc
,PreTrap
打印其消息并设置 QUIT 标志,但会myfunc
继续运行并且CleanUp
直到当前myfunc
运行完成后才会开始。
Note that my version of CleanUp
resets the QUIT flag. This prevents CleanUp
from running twice.
请注意,我的版本CleanUp
重置了 QUIT 标志。这可以防止CleanUp
运行两次。
This version removes the CleanUp
call from the main loop and puts it inside the PreTrap
function. It uses wait
with no ID argument in PreTrap
, which means we don't need to bother saving the PID of each child process. This should be ok since if we're in the trap we dowant to wait for all child processes to complete before proceeding.
此版本CleanUp
从主循环中删除调用并将其放入PreTrap
函数中。它在 中使用wait
没有 ID 参数PreTrap
,这意味着我们不需要费心保存每个子进程的 PID。这应该没问题,因为如果我们处于陷阱中,我们确实希望在继续之前等待所有子进程完成。
#!/bin/bash
# Yet another Trap demo...
myfunc()
{
echo -n "Starting :"
for i in {1..5}
do
echo -n " $i"
sleep 1
done
echo ". Finished "
}
PreTrap() { echo -n " in trap "; wait; CleanUp; }
CleanUp() {
[[ -n $CLEAN ]] && { echo bye; exit; }
echo "Cleaning up"
sleep 1
echo "... done!"
CLEAN=1
exit
}
trap PreTrap SIGINT SIGTERM SIGTSTP
trap "echo exittrap; CleanUp" EXIT
for i in {a..c}
do
#Run myfunc in background but wait until it completes.
myfunc "$i" & wait $!
done
We don't really need to do myfunc "$i" & wait $!
in this script, it could be simplified even further to myfunc "$i" & wait
. But generally it's better to wait for a specific PID just in case there's some other process running in the background that we don'twant to wait for.
我们真的不需要myfunc "$i" & wait $!
在这个脚本中做,它可以进一步简化为myfunc "$i" & wait
. 但一般最好是等待一个特定的PID,以防万一有在后台运行,我们一些其他进程不希望等待。
Note that pressing Ctrl-C while CleanUp
itself is running will interrupt the current foreground process (probably sleep
in this demo).
请注意,在CleanUp
其自身运行时按 Ctrl-C将中断当前的前台进程(可能sleep
在此演示中)。