获取睡眠命令在 shell 脚本中结束的剩余秒数

get the number of seconds left for the sleep command to end in a shell script

我构建了一个 shell 脚本,它会休眠指定的分钟数并在完成时显示通知。

TIME=$(zenity --scale --title="Next Session in (?) minutes")

sleep $TIME'm'

BEEP="/usr/share/sounds/freedesktop/stereo/complete.oga"
paplay $BEEP
notify-send "Next Session" "Press <Ctrl><Shift><s> to run the script again"

我在代码的开头使用基于文件的方法阻止了程序的多个实例的执行。当用户想要 运行 脚本而另一个实例正在 运行ning 时,它会显示脚本已经 运行ning 的通知。

LOCKFILE=/tmp/lock.txt
if [ -e ${LOCKFILE} ] && kill -0 `cat ${LOCKFILE}`; then
    notify-send "Already Running" $SECONDS
    exit
fi

trap "rm -f ${LOCKFILE}; exit" INT TERM EXIT
echo $$ > ${LOCKFILE}

最后删除脚本末尾的临时文件

rm -f ${LOCKFILE}

现在我想在通知中添加一个文本,告诉我 shell 脚本中的睡眠命令还有多少秒结束。 (更改已经 运行ning 通知如下)

notify-send "Already Running" $SECONDS

用自己控制的while循环实现sleep命令会影响计算机的整体性能。我认为 sleep 命令是一个更好的选择,因为它通过将自身发送到进程队列中的等待状态来优化进程。 有什么办法可以解决这个问题吗?

将脚本应该结束的时间存储在锁定文件中。

if [ -e "$LOCKFILE" ]; then
    read pid endtime < "$LOCKFILE"
    if kill -0 "$pid"; then
        notify-send "Already running" $(($(date +%s) - $endtime))
        exit
    fi
fi

trap "rm -f ${LOCKFILE}" EXIT   # Use cascaded trap
trap 'exit 127' INT TERM

echo $$ $(($(date +%s) + (60 * $TIME))) >"$LOCKFILE"

这里存在竞争条件;如果两个脚本几乎同时启动,当第二个启动时,第一个脚本可能在 if 内但在 echo 之前。如果您确实需要防止这种情况发生,请使用锁定目录而不是文件——目录创建是原子的,成功或失败仅在一个时间点发生(但随后您需要清除目录存在但不属于文件的神秘场景——可能是在粗心的 OOM 杀手或其他东西之后)。

我认为 Triplee 有一个很好的答案,另一种可以应用于任何可能阻塞的 运行 进程的处理方法是 bg 进程短暂地获取并保存分配的 pid $! 到文件然后 fg 过程返回。

从那里你可以计算并通过 ps 获得秒数:

TIME=$(zenity --scale --title="Next Session in (?) minutes")

SLEEP_PID_FILE="/tmp/__session_ui_sleep_pid__" 
sleep $TIME'm' &
echo $! >> "${SLEEP_PID_FILE}" 
fg 

BEEP="/usr/share/sounds/freedesktop/stereo/complete.oga"
paplay $BEEP
notify-send "Next Session" "Press <Ctrl><Shift><s> to run the script again"

然后你可以用类似的东西找到当前经过的时间:
notify-send "Already running for $(($(date +%s)-$(date -d"$(ps -o lstart= -p$(< "${SLEEP_PID_FILE}"))" +%s))) seconds..."