我有一个简单的脚本:
f=1 # initial value
loop(){
...
if [[ $f -eq 1 ]]
then
echo $msg1
f=2
(sleep $interval; loop) &
exit 1
elif [[ $f -eq 2 ]]
then
echo $msg2
f=1
(sleep $interval; loop) &
exit 2
fi
...
}
它的目的是在这两个消息之间一个接一个地循环。我这样做,而不是正常的 while 循环,因为我希望脚本处于后台。所以,我期望在我的终端中发生的是:
gt@gt:~./timer.sh
<msg1>
gt@gt:~<me running some other commands>
<some output>
gt@gt:~<as soon as $interval time gets over>
<msg2>
gt@gt:~<fresh prompt>
正如您所看到的,我希望 msg2 出现在正常回显输出出现的位置,并且紧接着它应该留下一个新的命令输入提示。但实际发生的情况是:
gt@gt:~./timer.sh
<msg1>
gt@gt:~<me running some other commands>
<some output>
gt@gt:~<msg2>
<same prompt is still waiting for input here>
如何解决这个问题?
您可以通过运行#!/bin/bash
并设置sleep=3
秒数在本地验证此问题。
答案1
让您的计时器脚本将SIGINT
信号发送到父进程 ( $PPID
)。
例子
#!/bin/bash
echo "Going to interrupt $PPID in 5 seconds"
sleep 5
echo "More output"
kill -SIGINT $PPID
sleep 5
当此脚本在后台 ( ./script &
) 运行时,它应该发送输出、睡眠,然后发送更多输出,然后强制重新绘制提示。
该脚本将再次休眠,然后退出。
更新OP的脚本
f=1 # initial value
loop(){
...
if [[ $f -eq 1 ]]
then
echo $msg1
kill -SIGINT $PPID
f=2
(sleep $interval; loop) &
exit 1
elif [[ $f -eq 2 ]]
then
echo $msg2
kill -SIGINT $PPID
f=1
(sleep $interval; loop) &
exit 2
fi
...
}
当您的 shell 进程被信号中断时,您将丢失待处理的输入行,但命令提示符将在计时器脚本输出后重新绘制。
编辑
要在子脚本中重现此行为,请将父 PID 环境变量从调用者脚本传递到子脚本中。
main.sh
...
./timer.sh $PPID
...
#exits
timer.sh
...
# Save the parent pid argument
gp=$1
...
# Replace instances of $PPID with $gp in timer.sh
...
kill -SIGINT $gp
答案2
我认为下面的shell脚本timer
会做你想做的事或者做一些接近你想做的事。
#!/bin/bash
interval=60 # seconds
tips="Skip waiting with Yes|Enter, exit with No|Escape"
msg1="First message ...
$tips"
msg2="Second message ...
$tips"
f=1 # initial value
while true
do
if [[ $f -eq 1 ]]
then
# zenity --notification --text="$msg1" 2> /dev/null
LANG=C zenity --question --title="${0##*/}" \
--text="$msg1" --timeout="$interval" --width=400 2> /dev/null
elif [[ $f -eq -1 ]]
then
# zenity --notification --text="$msg2" 2> /dev/null
LANG=C zenity --question --title="${0##*/}" \
--text="$msg2" --timeout="$interval" --width=400 2> /dev/null
fi
ans=$?
if [ $ans -eq 0 ] || [ $ans -eq 5 ]
then
f=$((-f))
else
zenity --info --title="${0##*/}" \
--text="Exiting now" --timeout="3" 2> /dev/null
break
fi
done & pid=$!
echo "kill with
kill $pid # useful during testing
--------------------------------------------"
我用
zenity --question
发送消息,这应该没问题,如果它们很少出现,并且您确实想注意到它们(并且可能做某事)。这样你也可以优雅地退出后台任务。取消注释
zenity --notification
zenity --question
命令,如果您想要较少侵入性的消息,请将命令注释掉。您还应该在各行
sleep $interval
之后输入命令行zenity --notification
。但这也意味着您必须使用其进程号来终止后台进程,
kill <process number>
当您启动计时器shell脚本时,会显示实际的进程号,您可以通过以下方式找到它
ps
$ ./timer kill with kill 15585 # useful during testing -------------------------------------------- $ ps -e |grep timer 15585 pts/3 00:00:00 timer