Я хочу, чтобы мой сценарий bash спал до определенного времени. Итак, мне нужна такая команда, как «сон», которая принимает не интервал, а время окончания и спит до этого момента.
«At» -daemon не является решением, так как мне нужно заблокировать работающий скрипт до определенной даты / времени.
Есть такая команда?
Ответы:
Как упомянул программист-преступник, я думаю, что решение - просто спать на нужное количество секунд.
Чтобы сделать это в 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
frombrew
→ см. Эти инструкцииисточник
target_epoch=$(date -d 'tomorrow 03:00' +%s)
вместо этого.date
вместо двух! Смотрите первую часть моего ответаПоскольку этот вопрос был задан 4 года назад, первая часть касается старых версий bash:
Последнее изменение: среда, 22 апреля 2020 г., что-то между 10:30 и 10:55 (важно для чтения образцов)
Общий метод (Избегайте бесполезных вилок!)
(Примечание: этот метод используется
date -f
не в POSIX и не работает под MacOS! Если под Mac, перейдите к моему чистомутрепатьфункция )Чтобы уменьшить
forks
, вместо того, чтобы запускатьdate
два раза, я предпочитаю использовать это:Простой стартовый образец
sleep $(($(date -f - +%s- <<< $'tomorrow 21:30\nnow')0))
где
tomorrow 21:30
может быть заменен любой датой и форматом, распознаваемымdate
в будущем.С высокой точностью (наносекунд)
Почти то же самое:
sleep $(bc <<<s$(date -f - +'t=%s.%N;' <<<$'07:00 tomorrow\nnow')'st-t')
Достигнув следующего раза
Для достижения следующего
HH:MM
значения сегодня, если возможно, завтра, если слишком поздно:sleep $((($(date -f - +%s- <<<$'21:30 tomorrow\nnow')0)%86400))
Это работает под трепать, кш и другие современные оболочки, но вы должны использовать:
sleep $(( ( $(printf 'tomorrow 21:30\nnow\n' | date -f - +%s-)0 )%86400 ))
под более легкими снарядами, такими какясень или рывок.
Чистый трепать Кстати, вилки нет !!
Проверено под MacOS!
Я написал
однудве маленькие функции:sleepUntil
иsleepUntilHires
Syntax: sleepUntil [-q] <HH[:MM[:SS]]> [more days] -q Quiet: don't print sleep computed argument HH Hours (minimal required argument) MM Minutes (00 if not set) SS Seconds (00 if not set) more days multiplied by 86400 (0 by default)
Поскольку новые версии bash предлагают
printf
возможность получения даты, для этого нового способа засыпать до ЧЧ: ММ без использованияdate
или какой-либо другой вилки, я немного построилтрепатьфункция. Вот:sleepUntil() { # args [-q] <HH[:MM[:SS]]> [more days] local slp tzoff now quiet=false [ "$1" = "-q" ] && shift && quiet=true local -a 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 + ${2:-0}*86400 )) $quiet || printf 'sleep %ss, -> %(%c)T\n' $slp $((now+slp)) sleep $slp }
Затем:
sleepUntil 10:37 ; date +"Now, it is: %T" sleep 49s, -> Wed Apr 22 10:37:00 2020 Now, it is: 10:37:00 sleepUntil -q 10:37:44 ; date +"Now, it is: %T" Now, it is: 10:37:44 sleepUntil 10:50 1 ; date +"Now, it is: %T" sleep 86675s, -> Thu Apr 23 10:50:00 2020 ^C
Если цель раньше, это будет спать до завтра:
sleepUntil 10:30 ; date +"Now, it is: %T" sleep 85417s, -> Thu Apr 23 10:30:00 2020 ^C sleepUntil 10:30 1 ; date +"Now, it is: %T" sleep 171825s, -> Fri Apr 24 10:30:00 2020 ^C
Время HiRes с трепать под GNU / Linux
Недавние трепать, с версии 5.0 добавлена новая
$EPOCHREALTIME
переменная с микросекундами. От этого естьsleepUntilHires
функция.sleepUntilHires () { # args [-q] <HH[:MM[:SS]]> [more days] local slp tzoff now quiet=false musec musleep; [ "$1" = "-q" ] && shift && quiet=true; local -a hms=(${1//:/ }); printf -v now '%(%s)T' -1; IFS=. read now musec <<< $EPOCHREALTIME; musleep=$[2000000-10#$musec]; 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+10#${hms[2]} - tzoff - now - 1 ) % 86400 ) + ${2:-0} * 86400 )).${musleep:1}; $quiet || printf 'sleep %ss, -> %(%c)T\n' $slp $((now+${slp%.*}+1)); read -t $slp foo }
Обратите внимание: это
read -t
встроенное использование вместоsleep
. К сожалению, это не сработает при работе в фоновом режиме без реального TTY. Не стесняйтесь заменитьread -t
на,sleep
если вы планируете запускать это в фоновых сценариях ... (Но для фонового процесса рассмотрите возможность использованияcron
и / илиat
вместо всего этого)Пропустите следующий абзац, чтобы узнать о тестах и предупреждениях
$ËPOCHSECONDS
!Последнее ядро не используется
/proc/timer_list
пользователем !!В недавнем ядре 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
для вычисления времени сна:Маленькая тестовая функция
tstSleepUntilHires () { local now next last printf -v next "%(%H:%M:%S)T" $((${EPOCHREALTIME%.*}+1)) sleepUntilHires $next date -f - +%F-%T.%N < <(echo now;sleep .92;echo now) printf -v next "%(%H:%M:%S)T" $((${EPOCHREALTIME%.*}+1)) sleepUntilHires $next date +%F-%T.%N }
Может отображать что-то вроде:
Не смешивайте
$EPOCHSECOND
и$EPOCHREALTIME
!Прочтите мое предупреждение о разнице между
$EPOCHSECOND
и$EPOCHREALTIME
Эта функция используется,
$EPOCHREALTIME
поэтому не используйте ее$EPOCHSECOND
для установки следующей секунды :Пример проблемы: попытка напечатать время с округлением на 2 секунды:
for i in 1 2;do printf -v nextH "%(%T)T" $(((EPOCHSECONDS/2)*2+2)) sleepUntilHires $nextH IFS=. read now musec <<<$EPOCHREALTIME printf "%(%c)T.%s\n" $now $musec done
Может производить:
источник
Используйте
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
Все сделано!
источник
-d
является расширением POSIX. В FreeBSD он пытается установить значение ядра для перехода на летнее время.Вот решение, которое выполняет свою работу И информирует пользователя о том, сколько времени осталось. Я использую его почти каждый день для запуска скриптов ночью (используя cygwin, так как я не мог
cron
работать с Windows)Характеристики
&&
Пробный запуск
(Дата в конце не является частью функции, а связана с
&& 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 }
источник
Вы можете остановить выполнение процесса, отправив ему сигнал SIGSTOP, а затем заставить его возобновить выполнение, отправив ему сигнал SIGCONT.
Таким образом, вы можете остановить свой скрипт, отправив SIGSTOP:
kill -SIGSTOP <pid>
Затем используйте 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 - название скрипта)
больше дней опаздывать на работу, потому что вы опечатали день. ура!
источник
Помните, что "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
источник
SIGNCHAR=${SECONDS:0:1}
а затемif [ "$SIGNCHAR" = "-" ]
. Это должно сработать.Вы можете рассчитать количество секунд между настоящим моментом и временем пробуждения и использовать существующую команду «сна».
источник
Возможно, вы могли бы использовать «at», чтобы послать сигнал вашему скрипту, который ждал этого сигнала.
источник
На самом деле я написал https://tamentis.com/projects/sleepuntil/ именно для этой цели. Это немного переборщило, большая часть кода взята из BSD 'at', поэтому он довольно совместим со стандартами:
источник
Вот что я только что написал для синхронизации нескольких тестовых клиентов:
#!/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
в другом.источник
until
установить конкретную дату / время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
источник
Для этого я собрал небольшую утилиту под названием Hypnos . Он настроен с использованием синтаксиса crontab и до этого времени блокируется.
#!/bin/bash while [ 1 ]; do hypnos "0 * * * *" echo "running some tasks..." # ... done
источник
Чтобы расширить основной ответ, вот несколько действительных примеров, касающихся манипуляции строкой даты:
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
источник
В OpenBSD можно использовать следующее, чтобы сжать
*/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
что для минутной спецификации требуется ровно две цифры.источник
Используйте tarry. Это инструмент, который я написал специально для этого.
https://github.com/metaphyze/tarry/
Это простой инструмент командной строки для ожидания определенного времени. Это не то же самое, что «сон», который ждет некоторое время. Это полезно, если вы хотите выполнить что-то в определенное время или, что более вероятно, выполнить несколько действий в одно и то же время, например, для тестирования, может ли сервер обрабатывать несколько очень одновременных запросов. Вы можете использовать это так с "&&" в Linux, Mac или Windows:
Это будет ждать до 16: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 доступны по ссылкам на странице.
источник