我希望我的bash脚本睡到特定时间.所以,我想要一个像"睡眠"这样的命令,它不需要间隔但是结束时间,直到那时才睡觉.
"at"-daemon不是解决方案,因为我需要阻止正在运行的脚本直到某个日期/时间.
有这样的命令吗?
Spo*_*ser 84
正如Outlaw Programmer所提到的,我认为解决方案只是睡眠正确的秒数.
要在bash中执行此操作,请执行以下操作:
current_epoch=$(date +%s)
target_epoch=$(date -d '01/01/2010 12:00' +%s)
sleep_seconds=$(( $target_epoch - $current_epoch ))
sleep $sleep_seconds
Run Code Online (Sandbox Code Playgroud)
要将精度降低到纳秒(实际上大约几毫秒),请使用以下语法:
current_epoch=$(date +%s.%N)
target_epoch=$(date -d "20:25:00.12345" +%s.%N)
sleep_seconds=$(echo "$target_epoch - $current_epoch"|bc)
sleep $sleep_seconds
Run Code Online (Sandbox Code Playgroud)
需要注意的是MACOS/OS X不支持低于秒的精度,你将需要使用coreutils
从brew
代替→ 请参阅这些说明
Joh*_*lla 19
使用sleep
,但使用计算时间date
.你会想要用date -d
它.例如,假设你想要等到下周:
expr `date -d "next week" +%s` - `date -d "now" +%s`
Run Code Online (Sandbox Code Playgroud)
只需将"下周"替换为您想要等待的日期,然后将此表达式赋值给一个值,然后休眠这么多秒:
startTime=$(date +%s)
endTime=$(date -d "next week" +%s)
timeToWait=$(($endTime- $startTime))
sleep $timeToWait
Run Code Online (Sandbox Code Playgroud)
全部完成!
F. *_*uri 15
正如4年前提出这个问题,第一部分涉及旧的 bash版本:
为了减少forks
而不是运行date
两次,我更喜欢使用它:
sleep $(($(date -f - +%s- <<< $'tomorrow 21:30\nnow')0))
Run Code Online (Sandbox Code Playgroud)
在tomorrow 21:30
将来可以用任何类型的日期和格式替换date
.
或者更好,如果可能的话今天达到下一个HH:MM
意义,明天如果为时已晚:
sleep $((($(date -f - +%s- <<<$'21:30 tomorrow\nnow')0)%86400))
Run Code Online (Sandbox Code Playgroud)
这适用于bash,ksh和其他现代shell,但你必须使用:
sleep $(( ( $(printf 'tomorrow 21:30\nnow\n' | date -f - +%s-)0 )%86400 ))
Run Code Online (Sandbox Code Playgroud)
至于我需要这种工具从未exceded 24小时,下面将只关心HH
,HH:MM
或HH:MM:SS
语法意味着在接下来的24小时.(无论如何,如果你需要更多,你甚至可以使用fork来返回旧方法date
.试图在运行多天的脚本中消除一个fork是过度的.)
由于新版本的bash确实提供了一个printf
检索日期的选项,对于这种新的睡眠方式直到HH:MM无需使用date
或任何其他分叉,我构建了一个小的bash函数.这里是:
sleepUntil() {
local slp tzoff now quiet=false
[ "$1" = "-q" ] && shift && quiet=true
local hms=(${1//:/ })
printf -v now '%(%s)T' -1
printf -v tzoff '%(%z)T\n' $now
tzoff=$((0${tzoff:0:1}(3600*${tzoff:1:2}+60*${tzoff:3:2})))
slp=$(((86400+(now-now%86400)+10#$hms*3600+10#${hms[1]}*60+${hms[2]}-tzoff-now)%86400))
$quiet || printf 'sleep %ss, -> %(%c)T\n' $slp $((now+slp))
sleep $slp
}
Run Code Online (Sandbox Code Playgroud)
然后:
sleepUntil 11:11 ; date +"Now, it is: %T"
sleep 3s, -> sam 28 sep 2013 11:11:00 CEST
Now, it is: 11:11:00
sleepUntil -q 11:11:5 ; date +"Now, it is: %T"
Now, it is: 11:11:05
Run Code Online (Sandbox Code Playgroud)
在最近的Linux内核中,您将找到一个名为的变量文件/proc/timer_list
,您可以在其中读取offset
和now
变量,以纳秒为单位.因此,我们可以计算睡眠时间,以达到最佳时间.
(我写这个来生成和跟踪非常大的日志文件上的特定事件,包含一千秒的一行).
mapfile </proc/timer_list _timer_list
for ((_i=0;_i<${#_timer_list[@]};_i++));do
[[ ${_timer_list[_i]} =~ ^now ]] && TIMER_LIST_SKIP=$_i
[[ ${_timer_list[_i]} =~ offset:.*[1-9] ]] && \
TIMER_LIST_OFFSET=${_timer_list[_i]//[a-z.: ]} && \
break
done
unset _i _timer_list
readonly TIMER_LIST_OFFSET TIMER_LIST_SKIP
sleepUntilHires() {
local slp tzoff now quiet=false nsnow nsslp
[ "$1" = "-q" ] && shift && quiet=true
local hms=(${1//:/ })
mapfile -n 1 -s $TIMER_LIST_SKIP nsnow </proc/timer_list
printf -v now '%(%s)T' -1
printf -v tzoff '%(%z)T\n' $now
nsnow=$((${nsnow//[a-z ]}+TIMER_LIST_OFFSET))
nsslp=$((2000000000-10#${nsnow:${#nsnow}-9}))
tzoff=$((0${tzoff:0:1}(3600*${tzoff:1:2}+60*${tzoff:3:2})))
slp=$(( ( 86400 + ( now - now%86400 ) +
10#$hms*3600+10#${hms[1]}*60+${hms[2]} -
tzoff - now - 1
) % 86400)).${nsslp:1}
$quiet || printf 'sleep %ss, -> %(%c)T\n' $slp $((now+${slp%.*}+1))
sleep $slp
}
Run Code Online (Sandbox Code Playgroud)
定义两个后只读变量,TIMER_LIST_OFFSET
并且TIMER_LIST_SKIP
,该功能将很快访问该变量文件/proc/timer_list
计算睡眠时间:
sleepUntilHires 15:03 ;date +%F-%T.%N ;sleep .97;date +%F-%T.%N
sleep 19.632345552s, -> sam 28 sep 2013 15:03:00 CEST
2013-09-28-15:03:00.003471143
2013-09-28-15:03:00.976100517
sleepUntilHires -q 15:04;date -f - +%F-%T.%N < <(echo now;sleep .97;echo now)
2013-09-28-15:04:00.003608002
2013-09-28-15:04:00.974066555
Run Code Online (Sandbox Code Playgroud)
最后
tstSleepUntilHires () {
local now next last
printf -v now "%(%s)T"
printf -v next "%(%H:%M:%S)T" $((now+1))
printf -v last "%(%H:%M:%S)T" $((now+2))
sleepUntilHires $next
date -f - +%F-%T.%N < <(echo now;sleep .92;echo now)
sleepUntilHires $last
date +%F-%T.%N
}
Run Code Online (Sandbox Code Playgroud)
可能会呈现如下内容:
sleep 0.155579469s, -> Mon Aug 20 20:42:51 2018
2018-08-20-20:42:51.005743047
2018-08-20-20:42:51.927112981
sleep 0.071764300s, -> Mon Aug 20 20:42:52 2018
2018-08-20-20:42:52.003165816
Run Code Online (Sandbox Code Playgroud)
Nota :( .92 + 0.071 = .991
在我的桌面上)
您可以通过向其发送SIGSTOP信号来停止执行进程,然后通过向其发送SIGCONT信号使其继续执行.
所以你可以通过发送一个SIGSTOP来停止你的脚本:
kill -SIGSTOP <pid>
Run Code Online (Sandbox Code Playgroud)
然后使用at deamon通过以相同的方式发送SIGCONT来唤醒它.
据推测,你的脚本会告诉你什么时候想要在醒来之前被唤醒.
在Ubuntu 12.04.4 LTS上,这是可以正常工作的简单bash输入:
sleep $(expr `date -d "03/21/2014 12:30" +%s` - `date +%s`)
Run Code Online (Sandbox Code Playgroud)
接下来是SpoonMeiser的回答,这是一个具体的例子:
$cat ./reviveself
#!/bin/bash
# save my process ID
rspid=$$
# schedule my own resuscitation
# /bin/sh seems to dislike the SIGCONT form, so I use CONT
# at can accept specific dates and times as well as relative ones
# you can even do something like "at thursday" which would occur on a
# multiple of 24 hours rather than the beginning of the day
echo "kill -CONT $rspid"|at now + 2 minutes
# knock myself unconscious
# bash is happy with symbolic signals
kill -SIGSTOP $rspid
# do something to prove I'm alive
date>>reviveself.out
$
Run Code Online (Sandbox Code Playgroud)
小智 5
我想要一个只检查小时和分钟的脚本,这样我每天都可以使用相同的参数运行该脚本。我不想担心明天是哪一天。因此,我使用了另一种方法。
target="$1.$2"
cur=$(date '+%H.%M')
while test $target != $cur; do
sleep 59
cur=$(date '+%H.%M')
done
Run Code Online (Sandbox Code Playgroud)
脚本的参数是小时和分钟,所以我可以这样写:
til 7 45 && mplayer song.ogg
Run Code Online (Sandbox Code Playgroud)
(直到是脚本的名称)
上班时间不再延迟,因为您输错了一天。干杯!
这是一个解决方案,可以完成工作并通知用户剩余时间。我几乎每天都在晚上使用它来运行脚本(使用cygwin,因为我无法cron
在Windows上工作)
&&
$ til 13:00 && date
1 hour and 18 minutes and 26 seconds left...
1 hour and 18 minutes left...
1 hour and 17 minutes left...
1 hour and 16 minutes left...
1 hour and 15 minutes left...
1 hour and 14 minutes left...
1 hour and 10 minutes left...
1 hour and 5 minutes left...
1 hour and 0 minutes left...
55 minutes left...
50 minutes left...
45 minutes left...
40 minutes left...
35 minutes left...
30 minutes left...
25 minutes left...
20 minutes left...
15 minutes left...
10 minutes left...
5 minutes left...
4 minutes left...
3 minutes left...
2 minutes left...
1 minute left...
Mon, May 18, 2015 1:00:00 PM
Run Code Online (Sandbox Code Playgroud)
(结尾的日期不是函数的一部分,而是由于引起的&& date
)
til(){
local hour mins target now left initial sleft correction m sec h hm hs ms ss showSeconds toSleep
showSeconds=true
[[ $1 =~ ([0-9][0-9]):([0-9][0-9]) ]] || { echo >&2 "USAGE: til HH:MM"; return 1; }
hour=${BASH_REMATCH[1]} mins=${BASH_REMATCH[2]}
target=$(date +%s -d "$hour:$mins") || return 1
now=$(date +%s)
(( target > now )) || target=$(date +%s -d "tomorrow $hour:$mins")
left=$((target - now))
initial=$left
while (( left > 0 )); do
if (( initial - left < 300 )) || (( left < 300 )) || [[ ${left: -2} == 00 ]]; then
# We enter this condition:
# - once every 5 minutes
# - every minute for 5 minutes after the start
# - every minute for 5 minutes before the end
# Here, we will print how much time is left, and re-synchronize the clock
hs= ms= ss=
m=$((left/60)) sec=$((left%60)) # minutes and seconds left
h=$((m/60)) hm=$((m%60)) # hours and minutes left
# Re-synchronise
now=$(date +%s) sleft=$((target - now)) # recalculate time left, multiple 60s sleeps and date calls have some overhead.
correction=$((sleft-left))
if (( ${correction#-} > 59 )); then
echo "System time change detected..."
(( sleft <= 0 )) && return # terminating as the desired time passed already
til "$1" && return # resuming the timer anew with the new time
fi
# plural calculations
(( sec > 1 )) && ss=s
(( hm != 1 )) && ms=s
(( h > 1 )) && hs=s
(( h > 0 )) && printf %s "$h hour$hs and "
(( h > 0 || hm > 0 )) && printf '%2d %s' "$hm" "minute$ms"
if [[ $showSeconds ]]; then
showSeconds=
(( h > 0 || hm > 0 )) && (( sec > 0 )) && printf %s " and "
(( sec > 0 )) && printf %s "$sec second$ss"
echo " left..."
(( sec > 0 )) && sleep "$sec" && left=$((left-sec)) && continue
else
echo " left..."
fi
fi
left=$((left-60))
sleep "$((60+correction))"
correction=0
done
}
Run Code Online (Sandbox Code Playgroud)