我希望我的bash脚本睡到特定时间。所以,我想要一个像“睡眠”这样的命令,它不会占用间隔,而是一个结束时间,直到那时才会睡觉。
“at”-daemon不是解决方案,因为我需要阻止正在运行的脚本直到某个日期/时间。
有这样的命令吗?
答案 0 :(得分:81)
正如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
而不是see these instructions
答案 1 :(得分:19)
使用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
全部完成!
答案 2 :(得分:13)
正如4年前提出这个问题,第一部分涉及旧 bash版本:
为了减少forks
,而不是两次运行date
,我更喜欢使用此功能:
sleep $(($(date -f - +%s- <<< $'tomorrow 21:30\nnow')0))
tomorrow 21:30
可以被date
未来识别的任何日期和格式替换。
或更好,如果可能的话,今天到达 next 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 ))
在较轻的 shell下
由于我对此类工具的需求从未超过24小时,因此以下仅在未来24小时内涉及HH
,HH:MM
或HH:MM:SS
语法含义 。 (无论如何,如果你需要更多,你甚至可以使用fork到date
返回旧方法。试图在运行多天的脚本中消除一个fork是过度的。)
新版本的bash确实提供printf
选项来检索日期,这种新方式 睡觉直到HH:MM 不使用{{1或者任何其他fork,我已经构建了一个小bash函数。这是:
date
然后:
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
}
在最近的Linux内核中,您将找到一个名为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
的变量文件,您可以在纳秒中读取/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
}
和TIMER_LIST_OFFSET
之后,该函数将非常快速地访问变量文件TIMER_LIST_SKIP
以计算睡眠时间:< / p>
/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
}
Nota: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
(在我的桌面上)
答案 3 :(得分:8)
您可以通过向其发送SIGSTOP信号来停止执行进程,然后通过向其发送SIGCONT信号使其恢复执行。
所以你可以通过发送SIGSTOP来停止你的脚本:
kill -SIGSTOP <pid>
然后使用at deamon通过以相同方式发送SIGCONT来唤醒它。
据推测,你的剧本会告诉你什么时候想要在醒来之前被唤醒。
答案 4 :(得分:6)
关注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
$
答案 5 :(得分:6)
在Ubuntu 12.04.4上,这里的LTS是简单的bash输入:
sleep $(expr `date -d "03/21/2014 12:30" +%s` - `date +%s`)
答案 6 :(得分:4)
我想要一个只检查小时和分钟的脚本,这样我每天都可以使用相同的参数运行脚本。我不想担心明天会是哪一天。所以我采用了不同的方法。
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
(til是脚本的名称)
上班迟到的日子不会因为你输错了这一天。干杯!
答案 7 :(得分:4)
这是一个完成工作的解决方案,并告知用户剩余的时间。
我几乎每天都在使用它来运行脚本(使用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
(结尾的日期不是函数的一部分,但由于&& 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
}
答案 8 :(得分:3)
timeToWait = $(($ end - $ start))
请注意“timeToWait”可能是负数! (例如,如果你指定睡到“15:57”,现在它是“15:58”)。所以你必须检查它以避免奇怪的消息错误:
#!/bin/bash
set -o nounset
### // Sleep until some date/time.
# // Example: sleepuntil 15:57; kdialog --msgbox "Backup needs to be done."
error() {
echo "$@" >&2
exit 1;
}
NAME_PROGRAM=$(basename "$0")
if [[ $# != 1 ]]; then
error "ERROR: program \"$NAME_PROGRAM\" needs 1 parameter and it has received: $#."
fi
current=$(date +%s.%N)
target=$(date -d "$1" +%s.%N)
seconds=$(echo "scale=9; $target - $current" | bc)
signchar=${seconds:0:1}
if [ "$signchar" = "-" ]; then
error "You need to specify in a different way the moment in which this program has to finish, probably indicating the day and the hour like in this example: $NAME_PROGRAM \"2009/12/30 10:57\"."
fi
sleep "$seconds"
# // End of file
答案 9 :(得分:1)
您也许可以使用'at'向您的脚本发送信号,该脚本等待该信号。
答案 10 :(得分:1)
您可以计算从现在到唤醒时间之间的秒数,并使用现有的“睡眠”命令。
答案 11 :(得分:1)
function sleepuntil() {
local target_time="$1"
today=$(date +"%m/%d/%Y")
current_epoch=$(date +%s)
target_epoch=$(date -d "$today $target_time" +%s)
sleep_seconds=$(( $target_epoch - $current_epoch ))
sleep $sleep_seconds
}
target_time="11:59"; sleepuntil $target_time
答案 12 :(得分:1)
我整理了一个名为Hypnos的小工具来完成此操作。它使用crontab语法进行配置,直到那时为止。
#!/bin/bash
while [ 1 ]; do
hypnos "0 * * * *"
echo "running some tasks..."
# ...
done
答案 13 :(得分:0)
为扩展主要答案,以下是一些有关日期字符串操作的有效示例:
sleep $(($(date -f - +%s- <<< $'+3 seconds\nnow')0)) && ls
sleep $(($(date -f - +%s- <<< $'3 seconds\nnow')0)) && ls
sleep $(($(date -f - +%s- <<< $'3 second\nnow')0)) && ls
sleep $(($(date -f - +%s- <<< $'+2 minute\nnow')0)) && ls
sleep $(($(date -f - +%s- <<< $'tomorrow\nnow')0)) && ls
sleep $(($(date -f - +%s- <<< $'tomorrow 21:30\nnow')0)) && ls
sleep $(($(date -f - +%s- <<< $'3 weeks\nnow')0)) && ls
sleep $(($(date -f - +%s- <<< $'3 week\nnow')0)) && ls
sleep $(($(date -f - +%s- <<< $'next Friday 09:00\nnow')0)) && ls
sleep $(($(date -f - +%s- <<< $'2027-01-01 00:00:01 UTC +5 hours\nnow')0)) && ls
答案 14 :(得分:0)
为了这个目的,我实际上写了https://tamentis.com/projects/sleepuntil/。它有点过度杀死大部分代码来自BSD&#39; at&#39;所以它符合标准:
$ sleepuntil noon && sendmail something
答案 15 :(得分:0)
这是我刚才写的用于同步多个测试客户端的内容:
#!/usr/bin/python
import time
import sys
now = time.time()
mod = float(sys.argv[1])
until = now - now % mod + mod
print "sleeping until", until
while True:
delta = until - time.time()
if delta <= 0:
print "done sleeping ", time.time()
break
time.sleep(delta / 2)
此脚本会一直睡到下一个“四舍五入”或“尖锐”的时间。
一个简单的用例是在一个终端中运行./sleep.py 10; ./test_client1.py
,在另一个终端中运行./sleep.py 10; ./test_client2.py
。
答案 16 :(得分:-2)
使用柏油。这是我为此专门编写的工具。
https://github.com/metaphyze/tarry/
这是一个简单的命令行工具,用于等待特定时间。这与等待一段时间的“睡眠”不同。如果您想在特定时间执行某件事,或者更有可能在完全相同的时间执行几件事,例如测试服务器是否可以处理多个非常同时的请求,则这很有用。您可以在Linux,Mac或Windows上将其与“ &&”一起使用:
tarry -until=16:03:04 && someOtherCommand
这将等到下午4:03:04,然后执行someOtherCommand。这是一个Linux / Mac示例,该示例演示如何运行所有计划在同一时间启动的多个请求:
for request in 1 2 3 4 5 6 7 8 9 10
do
tarry -until=16:03:04 && date > results.$request &
done
Ubuntu,Linux和Windows二进制文件可通过页面上的链接获得。