我希望我的bash脚本睡到特定时间.所以,我想要一个像"睡眠"这样的命令,它不需要间隔但是结束时间,直到那时才睡觉.
"at"-daemon不是解决方案,因为我需要阻止正在运行的脚本直到某个日期/时间.
有这样的命令吗?
正如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
要将精度降低到纳秒(实际上大约几毫秒),请使用以下语法:
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
需要注意的是MACOS/OS X不支持低于秒的精度,你将需要使用coreutils
从brew
代替→ 请参阅这些说明
使用sleep
,但使用计算时间date
.你会想要用date -d
它.例如,假设你想要等到下周:
expr `date -d "next week" +%s` - `date -d "now" +%s`
只需将"下周"替换为您想要等待的日期,然后将此表达式赋值给一个值,然后休眠这么多秒:
startTime=$(date +%s) endTime=$(date -d "next week" +%s) timeToWait=$(($endTime- $startTime)) sleep $timeToWait
全部完成!
正如4年前提出这个问题,第一部分涉及旧的 bash版本:
一般方法为了减少forks
而不是运行date
两次,我更喜欢使用它:
sleep $(($(date -f - +%s- <<< $'tomorrow 21:30\nnow')0))
在tomorrow 21:30
将来可以用任何类型的日期和格式替换date
.
或者更好,如果可能的话今天达到下一个HH:MM
意义,明天如果为时已晚:
sleep $((($(date -f - +%s- <<<$'21:30 tomorrow\nnow')0)%86400))
这适用于bash,ksh和其他现代shell,但你必须使用:
sleep $(( ( $(printf 'tomorrow 21:30\nnow\n' | date -f - +%s-)0 )%86400 ))
在较轻的外壳下如灰烬或破折号.
新的bash方式(没有叉子)至于我需要这种工具从未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 }
然后:
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:05HiRes 在GNU/Linux下使用bash的时间
在最近的Linux内核中,您将找到一个名为的变量文件/proc/timer_list
,您可以在其中读取offset
和now
变量,以纳秒为单位.因此,我们可以计算睡眠时间,以达到最佳时间.
(我写这个来生成和跟踪非常大的日志文件上的特定事件,包含一千秒的一行).
mapfile %(%c)T\n' $slp $((now+${slp%.*}+1)) sleep $slp }
定义两个后只读变量,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
最后
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 }
可能会呈现如下内容:
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
在下一秒开始,
打印时间,然后
等等0.92秒
打印时间,然后
计算0.07秒,到下一秒
然后睡0.07秒
打印时间.
Nota :( .92 + 0.071 = .991
在我的桌面上)
您可以通过向其发送SIGSTOP信号来停止执行进程,然后通过向其发送SIGCONT信号使其继续执行.
所以你可以通过发送一个SIGSTOP来停止你的脚本:
kill -SIGSTOP
然后使用at deamon通过以相同的方式发送SIGCONT来唤醒它.
据推测,你的脚本会告诉你什么时候想要在醒来之前被唤醒.
在Ubuntu 12.04.4 LTS上,这是可以正常工作的简单bash输入:
sleep $(expr `date -d "03/21/2014 12:30" +%s` - `date +%s`)
接下来是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 $
我想要一个只检查小时和分钟的脚本,这样我每天都可以使用相同的参数运行该脚本。我不想担心明天是哪一天。因此,我使用了另一种方法。
target="$1.$2" cur=$(date '+%H.%M') while test $target != $cur; do sleep 59 cur=$(date '+%H.%M') done
脚本的参数是小时和分钟,所以我可以这样写:
til 7 45 && mplayer song.ogg
(直到是脚本的名称)
上班时间不再延迟,因为您输错了一天。干杯!
这是一个解决方案,可以完成工作并通知用户剩余时间。我几乎每天都在晚上使用它来运行脚本(使用cygwin,因为我无法cron
在Windows上工作)
精确到秒
检测系统时间变化并适应
智能输出告诉您还剩多少时间
24小时输入格式
返回true以能够与 &&
样品运行
$ 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
(结尾的日期不是函数的一部分,而是由于引起的&& 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 }