bash 如何捕获 Ctrl-C 并使用它正确退出无限循环
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20071030/
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
How to capture Ctrl-C and use it to exit an endless loop properly
提问by localhost
I'm trying to run a program inside an endless loop because it sometimes dies for no reason. I would like to be able to hit Ctrl-C to prevent the program being restarted though.
我试图在一个无限循环中运行一个程序,因为它有时会无缘无故地死掉。我希望能够按 Ctrl-C 以防止程序重新启动。
I don't want Ctrl-C to kill the program, just to wait until it dies, then not restart it again.
我不希望 Ctrl-C 杀死程序,只是等到它死掉,然后再不重新启动它。
theprogram
is a wine program (utorrent).
theprogram
是一个葡萄酒程序(utorrent)。
Bonus points for telling me how to make it so it will safely exit theprogram
just like clicking on the 'x' in the top right of it. When I manually kill
it from the command line or hit Ctrl-C, it doesn't get to run its cleanup code. Hence my attempt to just stop it being restarted.
告诉我如何制作它的奖励积分,以便它可以theprogram
像单击右上角的“x”一样安全退出。当我kill
从命令行手动执行它或按 Ctrl-C 时,它无法运行其清理代码。因此,我试图阻止它重新启动。
I checked a few of the other questions about trapping SIGINT, but I couldn't work out how to do this.
我检查了一些关于捕获 SIGINT 的其他问题,但我不知道如何做到这一点。
Can anyone fix this code? My code seems to kill theprogram
then exit the loop when Ctrl-C is pressed, without letting theprogram
clean up.
任何人都可以修复此代码吗?我的代码似乎theprogram
在按下 Ctrl-C 时杀死然后退出循环,而没有theprogram
清理。
#!/bin/bash
EXIT=0
trap exiting SIGINT
exiting() { echo "Ctrl-C trapped, will not restart utorrent" ; EXIT=1;}
while [ $EXIT -eq 0 ] ; do
wine theprogram
echo "theprogram killed or finished"
date
echo "exit code $?"
echo "sleeping for 20 seconds, then restarting theprogram..."
sleep 20
done
echo "out of loop"
回答by Alfe
Try this:
尝试这个:
while true
do
xterm -e wine theprogram || break
sleep 3
done
The trick is done by using another xterm to start the wine. That way the wine has a different controlling tty and won't be affected by the Ctrl-cpress.
这个技巧是通过使用另一个 xterm 来启动 wine 来完成的。这样酒就有不同的控制 tty 并且不会受到Ctrl-c印刷机的影响。
The drawback is that there will be an additional xterm lingering around on your desktop. You could use the option -iconic
to start it iconified.
缺点是您的桌面上会有一个额外的 xterm。您可以使用该选项-iconic
来启动它图标化。
回答by localhost
Well, I ended up not using Ctrl-C as per my question because I couldn't find a good solution, but I used zenity
to popup a box that I can click to exit the loop:
好吧,我最终没有按照我的问题使用 Ctrl-C,因为我找不到一个好的解决方案,但我曾经zenity
弹出一个框,我可以单击它退出循环:
#!/bin/bash
zenity --info --title "thewineprogram" --text "Hit OK to disable thewineprogram auto-restart" & # run zenity in the background
zen_pid=$!
while :
do
wine <wineprogramlocation>
EXITCODE=$?
echo "thewineprog killed or finished"
echo "exit code was $EXITCODE"
date
kill -0 $zen_pid > /dev/null 2>&1 # kill -0 just checks if a pid exists
if [ $? -eq 1 ] # process does not exist
then
break
fi
echo "sleeping for 5 seconds, then restarting the wine program..."
sleep 5
done
echo "finished"
回答by Curtis Yallop
Use a monitoring process:
使用监控流程:
This allows the SIGINT signal to hit the monitor process trap handler without affecting the child.
这允许 SIGINT 信号在不影响子进程的情况下命中监视器进程陷阱处理程序。
(this could also be done in perl, python or any language)
(这也可以用 perl、python 或任何语言完成)
#!/bin/bash
cmd() {
trap '' INT
trap 'echo "Signal USR1 received (pid=$BASHPID)"; EXIT=1' USR1
EXIT=0
while [ $EXIT -eq 0 ]
do
echo "Starting (pid=$BASHPID)..."
sleep 5 # represents "wine theprogram"
echo "theprogram killed or finished"
date
echo "Exit code $?"
if [ $EXIT -eq 0 ]; then
echo "Sleeping for 2 seconds, then restarting theprogram..."
sleep 2
fi
done
echo "Exiting (pid=$BASHPID)"
}
run() { cmd & PID=$!; echo Started $PID; }
graceful_exit() { kill -s USR1 $PID && echo "$PID signalled to exit (USR1)"; }
shutdown() { kill -0 $PID 2>/dev/null && echo "Unexpected exit, killing $PID" && kill $PID; }
trap 'graceful_exit' INT
trap 'shutdown' EXIT
run
while :
do
wait && break
done
echo "Exiting monitor process"
回答by Curtis Yallop
It appears that trap on SIGINT must terminate the currently executing sub-command. The only exception appears to be the empty-string handler.
SIGINT 上的陷阱似乎必须终止当前正在执行的子命令。唯一的例外似乎是空字符串处理程序。
To demonstrate this: When ctrl-c is pressed this (trap "" INT;echo 1;sleep 5;echo 2)
does not halt the sleep command. However this (trap "echo hi" INT;echo 1;sleep 5;echo 2)
does. After this trap handler executes, execution continues on the command that follows, specifically "echo 2". So empty-string as a handler seems to be a special case which does not kill the current sub-command. There seems to be no way to run a handler plus not kill the current sub-command.
为了证明这一点:当按下 ctrl-c 时,这(trap "" INT;echo 1;sleep 5;echo 2)
不会停止 sleep 命令。然而这(trap "echo hi" INT;echo 1;sleep 5;echo 2)
确实如此。执行此陷阱处理程序后,将继续执行随后的命令,特别是“echo 2”。因此,作为处理程序的空字符串似乎是一种特殊情况,它不会终止当前的子命令。似乎没有办法运行处理程序加上不终止当前子命令。
Why this happens: Shell forks + execs to execute each program. On system call exec, it resets signal handlers to their default behavior (calling process is overwritten so the handlers are gone). Ignored signals are inherited (see "man 2 execve", "man 7 signal" and POSIX.1; http://www.perlmonks.org/?node_id=1198044)
为什么会发生这种情况:Shell fork + execs 来执行每个程序。在系统调用 exec 上,它将信号处理程序重置为其默认行为(调用进程被覆盖,因此处理程序消失)。忽略的信号被继承(参见“man 2 execve”、“man 7 signal”和POSIX.1;http://www.perlmonks.org/?node_id =1198044 )
I had a second idea: use 'trap "" INT' to fully disable ctrl-c and then trap ctrl-z as the signal to gracefully exit your program. Only trapping ctrl-z (STP) seems to not work properly for me. When I run '(trap "echo test" TSTP;sleep 5)' and press ctrl-z, my shell is hung. sleep never completes after 5 seconds and oddly ctrl-c no longer works. I don't know any other hotkey-signals to use other than ctrl-c and ctrl-z. This is known behavior: see Bash script: can not properly handle SIGTSTP.
我有第二个想法:使用 'trap "" INT' 完全禁用 ctrl-c,然后将 ctrl-z 作为信号优雅地退出程序。只有捕获 ctrl-z (STP) 对我来说似乎不能正常工作。当我运行 '(trap "echo test" TSTP;sleep 5)' 并按 ctrl-z 时,我的外壳挂了。sleep 在 5 秒后永远不会完成,奇怪的是 ctrl-c 不再起作用。除了 ctrl-c 和 ctrl-z 之外,我不知道可以使用任何其他热键信号。这是已知行为:请参阅Bash 脚本:无法正确处理 SIGTSTP。