Bash: спящий режим до определенного времени/даты
Я хочу, чтобы мой bash script спал до определенного времени. Итак, мне нужна команда типа "sleep", которая не занимает интервала, а времени окончания и до тех пор остается.
"at" -daemon не является решением, так как мне нужно заблокировать запущенный script до определенной даты/времени.
Есть ли такая команда?
Ответы
Ответ 1
Как упоминалось программистом Outlaw, я думаю, что решение - просто спать в течение правильного количества секунд.
Для этого в 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
from brew
вместо → см. эти инструкции
Ответ 2
Используйте 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
Все сделано!
Ответ 3
Вы можете остановить процесс от выполнения, отправив ему сигнал SIGSTOP, а затем заставить его возобновить выполнение, отправив ему сигнал SIGCONT.
Итак, вы можете остановить свой script, отправив SIGSTOP:
kill -SIGSTOP <pid>
И затем используйте функцию deamon, чтобы разбудить ее, отправив ей SIGCONT таким же образом.
Предположительно, ваш script будет информировать о том, когда он хочет проснуться, прежде чем начать спать.
Ответ 4
Как этот вопрос был задан 4 года назад, эта первая часть касается старой версии bash:
Общий метод
Чтобы уменьшить forks
, вместо двухкратного запуска date
, я предпочитаю использовать это:
sleep $(($(date -f - +%s- <<< $'tomorrow 21:30\nnow')0))
где tomorrow 21:30
можно заменить любым видом даты и формата, распознанным date
, в futur.
или лучше, для достижения следующего HH:MM
означает сегодня, если возможно, завтра, если слишком поздно:
sleep $((($(date -f - +%s- <<<$'21:30 tomorrow\nnow')0)%86400))
Эта работа под bash, ksh и другие современные оболочки, но вы должны Применение:
sleep $(( ( $(printf 'tomorrow 21:30\nnow\n' | date -f - +%s-)0 )%86400 ))
если под более легкой оболочкой, например ash или dash.
Новый bash (нет вилки )
Поскольку моя потребность в таком инструменте никогда не выходила за 24 часа, следующее будет касаться только синтаксиса HH
, HH:MM
или HH:MM:SS
в следующие 24 часа. (В любом случае, если вам нужно больше, вы даже можете вернуться к старому методу, используя fork для date
. Попытка уничтожить одну вилку в script, запущенной в течение многих дней, является чрезмерной.)
Поскольку новая версия bash предлагает printf
вариант для получения даты, для этого нового способа спящего режима до HH: MM без использования date
или любой другой вилки, ve построить немного 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:05
Время HiRes под GNU/Linux
В последнем ядре 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
}
После определения двух переменных только для чтения: 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
И окончательно
sleepUntilHires -q 15:11;date -f - +%F-%T.%N < <(echo now;sleep .97;echo now);sleepUntilHires 15:12;date +%F-%T.%N
2013-09-28-15:11:00.003973984
2013-09-28-15:11:00.974306364
sleep 59.024430416s, -> sam 28 sep 2013 15:12:00 CEST
2013-09-28-15:12:00.003447890
Где, в течение 1 минуты, есть:
60 сек -.97 сек (сон) - 59.024430416sec (сон) =.005569584
Для создания вилки date
существует менее 1/100-й секунды, чтение таймеров и вычисление времени сна.
sleepUntilHires -q 16:16;date -f - +%F-%T.%N < <(echo now;sleep .97;echo now);sleepUntilHires 16:16:01;date +%F-%T.%N
2013-09-28-16:16:00.003657646
2013-09-28-16:16:00.974469831
sleep 0.024204370s, -> Sat Sep 28 16:16:01 2013
2013-09-28-16:16:01.003133202
Ответ 5
Чтобы ответить на ответ 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
$
Ответ 6
Мне нужен script, который проверял только часы и минуты, чтобы я мог запускать script с одинаковыми параметрами каждый день. Я не хочу беспокоиться о том, какой день будет завтра. Поэтому я использовал другой подход.
target="$1.$2"
cur=$(date '+%H.%M')
while test $target != $cur; do
sleep 59
cur=$(date '+%H.%M')
done
параметры script - это часы и минуты, поэтому я могу написать что-то вроде:
til 7 45 && mplayer song.ogg
(til - имя script)
больше дней на работе, потому что вы не поняли день. ура!
Ответ 7
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 "[email protected]" >&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
Ответ 8
В Ubuntu 12.04.4 LTS представлен простой ввод bash, который работает:
sleep $(expr `date -d "03/21/2014 12:30" +%s` - `date +%s`)
Ответ 9
Вот решение, которое выполняет задание И информирует пользователя о том, сколько времени осталось.
Я использую его почти каждый день, чтобы запускать сценарии в течение ночи (используя cygwin, поскольку я не мог получить cron
для работы с окнами)
Функции
- Точность до второго
- Обнаруживает изменения системного времени и адаптирует
- Интеллектуальный вывод, показывающий, сколько осталось времени.
- 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
}
Ответ 10
Вы можете рассчитать количество секунд между временем пробуждения и использовать существующую команду "sleep".
Ответ 11
Возможно, вы можете использовать 'at' для отправки сигнала на ваш script, который сидел в ожидании этого сигнала.
Ответ 12
Я собрал небольшую утилиту Hypnos, чтобы сделать это. Он настраивается с использованием синтаксиса crontab и блоков до этого времени.
#!/bin/bash
while [ 1 ]; do
hypnos "0 * * * *"
echo "running some tasks..."
# ...
done
Ответ 13
Вот что я написал сейчас, чтобы синхронизировать несколько тестовых клиентов:
#!/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)
Этот script спит до следующего "округленного" или "резкого" времени.
Простым вариантом является запуск ./sleep.py 10; ./test_client1.py
в одном терминале и ./sleep.py 10; ./test_client2.py
в другом.
Ответ 14
В OpenBSD для сжатия a */5
5-минутного crontab(5)
можно использовать следующее: в 00
почасовую (чтобы убедиться, что генерируется меньше сообщений электронной почты, все они выполняют одну и ту же задачу с точными интервалами):
#!/bin/sh -x
for k in $(jot 12 00 55)
do
echo $(date) doing stuff
sleep $(expr $(date -j +%s $(printf %02d $(expr $k + 5))) - $(date -j +%s))
done
Обратите внимание, что date(1)
также нарушит sleep(1)
по дизайну на последней итерации, так как 60
минуты недействительны (если это не так!), поэтому нам не нужно ждать дополнительного времени до получения нашего отчета по электронной почте.
Также обратите внимание, что если одна из итераций займет более 5 минут, то sleep
также любезно провалится по дизайну, не спал вообще (из-за того, что отрицательное число интерпретируется как опция командной строки, вместо того, чтобы обертываться до следующего часа или даже вечности), тем самым гарантируя, что ваша работа может быть завершена в течение часа, отведенного (например, если только одна из итераций занимает немного больше 5 минут, тогда мы все равно будем иметь время догонять, без каких-либо обертываний до следующего часа).
printf(1)
требуется, потому что date
ожидает ровно две цифры для спецификации минут.
Ответ 15
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
Ответ 16
Я действительно написал https://tamentis.com/projects/sleepuntil/ для этой цели. Это немного более-убивает большую часть кода, исходя из BSD 'at', поэтому он довольно стандартно-совместимый:
$ sleepuntil noon && sendmail something