我有一个循环文件的 shell 脚本。处理每个文件可能需要几个小时(受 CPU/GPU 限制)。有时我想在循环结束时暂停执行,当文件处理完成时,然后能够稍后恢复,类似于使用 ctrl-z 和 then 暂停的方式fg
,但我希望它完成在实际暂停/进入后台之前当前循环。
基本上,我希望脚本在收到 ctrl-z (或类似的)时,等到它到达某一行后再停止。你如何实现这一目标?
答案1
我会用类似的东西:
#!/bin/bash
function catch_sig ()
{
interrupted=1
trap catch_sig SIGUSR1
}
interrupted=0
trap catch_sig SIGUSR1
echo "kill -s SIGUSR1 $$ to pause"
while [[ there is more to do ]] ; do
long_running_task
if [[ 1 -eq "$interrupted" ]] ; then
interrupted=0
read -p "You rang? " junk
fi
done
然后,在另一个终端中执行kill -s SIGUSR1
它告诉你的 PID。
请阅读man stty
以查看是否有您想要使用的信号。读man signal kill pkill bash
。
答案2
解决方案
基本上,我希望脚本在收到 ctrl-z (或类似的)时,等到它到达某一行后再停止。
如果我是你,我会让特定行检查特定文件是否存在。如果文件存在,则脚本应向自身发送 SIGSTOP。
[ -e /path/to/file ] && kill -s STOP "$$"
我使用文件而不是信号的原因是你不能(或者至少不能轻易)撤销信号。对于文件来说,这很简单;如果您改变主意,只需删除该文件,脚本就不会自行停止。
脚本自行停止后,向其发送 SIGCONT,它将继续。在实践中,您很可能会从启用作业控制的交互式 shell 运行脚本;在这种情况下,shell 将检测脚本何时停止,您将能够随意fg
运行bg
脚本。
脚本是否尝试自动删除文件或允许文件保留并在下一次迭代中导致 SIGSTOP 取决于您,直到你删除该文件。
Notekill -s STOP "$$"
不等于Ctrl+ z。击键会导致终端(除非配置不同)将 SIGTSTP(而不是 SIGSTOP)发送到整个前台进程组。如果您确定解释脚本的 shell 是其进程组的领导者,那么您可能需要kill -s TSTP -- "-$$"
.如果您的脚本启动也在进程组中的异步进程并且您也想停止它们,则向该组发送信号将会产生影响。当进程收到 SIGSTOP(不能被捕获、阻止或忽略)或 SIGTSTP(可以)时,它们的行为可能会有所不同。kill …
根据您的需要调整命令。
概念证明
以下脚本是否会自行停止,具体取决于 是否存在/tmp/blocker
,但它只会在循环迭代结束时执行此操作for
,而不是在中间执行。
#!/bin/sh -
for i in 1 2 3 4 5; do
echo "processing $i"
sleep 5
echo "still processing"
sleep 5
echo "and processing some more"
sleep 5
echo "done processing $i"
[ -e /tmp/blocker ] && kill -s STOP "$$"
done