Баш: спать до определенного времени/даты



Я хочу, чтобы мой сценарий bash спал до определенного времени. Итак, я хочу команду, такую как "сон", которая не занимает никакого интервала, но время окончания и спит до тех пор.



демон "at"-это не решение, так как мне нужно заблокировать запущенный скрипт до определенной даты/времени.



есть ли такая команда?

748   16  

16 ответов:

как упоминалось программистом 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 С 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))

это работает под Баш,КШ и другие современные снаряды, но вы должны использовать:

sleep $(( ( $(printf 'tomorrow 21:30\nnow\n' | date -f - +%s-)0 )%86400 ))

под легче снаряды как Ясень или тире.

новая Баш способ (без вилки)

поскольку моя потребность в таком инструменте никогда не превышала 24 часов, следующее будет касаться только HH,HH:MM или HH:MM:SS синтаксис значит в ближайшие 24 часа. (В любом случае, если вам нужно больше, вы можете даже вернуться к старому методу, используя вилку в date. Попытка устранить одну вилку в сценарии, работающем в течение многих дней, является излишней.)

как новые версии bash предлагают printf возможность получить дату, для этого нового способа спать до чч:мм без использования date или любая другая вилка, я построил немного Баш

вы можете остановить выполнение процесса, отправив ему сигнал SIGSTOP, а затем заставить его возобновить выполнение, отправив ему сигнал SIGCONT.

таким образом, вы можете остановить свой скрипт, отправив SIGSTOP:

kill -SIGSTOP <pid>

а затем используйте at deamon, чтобы разбудить его, отправив ему SIGCONT таким же образом.

предположительно, ваш скрипт сообщит о том, когда он хотел проснуться, прежде чем уснуть.

чтобы следовать ответу 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="."
cur=$(date '+%H.%M')
while test $target != $cur; do
    sleep 59
    cur=$(date '+%H.%M')
done

параметры скрипта-это часы и минуты, поэтому я могу написать что-то вроде:

til 7 45 && mplayer song.ogg

(til - это имя скрипта)

нет больше дней на работе, потому что вы ошиблись в день. ура!

на Ubuntu 12.04.4 LTS вот простой вход bash, который работает:

sleep $(expr `date -d "03/21/2014 12:30" +%s` - `date +%s`)

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 "")

if [[ $# != 1 ]]; then
     error "ERROR: program \"$NAME_PROGRAM\" needs 1 parameter and it has received: $#." 
fi


current=$(date +%s.%N)
target=$(date -d "" +%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

вот решение, которое выполняет эту работу и информирует пользователя о том, сколько времени осталось. Я использую его почти каждый день для запуска скриптов в ночное время (используя 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
  [[  =~ ([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 "" && 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
}

вы можете рассчитать количество секунд между настоящим моментом и временем пробуждения и использовать существующую команду "sleep".

возможно, вы могли бы использовать 'at', чтобы отправить сигнал вашему сценарию, который сидел в ожидании этого сигнала.

Я собрал небольшую утилиту под названием Гипнос для этого. Он настроен с использованием синтаксиса crontab и блоков до этого времени.

#!/bin/bash
while [ 1 ]; do
  hypnos "0 * * * *"
  echo "running some tasks..."
  # ...
done

вот что я только что написал, чтобы синхронизировать несколько тестовых клиентов:

#!/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 в другой.

On 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 минут, то у нас все равно будет время наверстать упущенное, ничего не оборачиваясь к следующему часу).

The printf(1) нужно, потому что date ожидает ровно две цифры Для минут спецификация.

 function sleepuntil() {
  local target_time=""
  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

Я на самом деле написал https://tamentis.com/projects/sleepuntil/ именно для этой цели. Это немного перебивает большую часть кода, поступающего из BSD 'at', поэтому он довольно стандартно совместим:

$ sleepuntil noon && sendmail something

Comments

    Ничего не найдено.