Śpij do określonej godziny / daty

89

Chcę, aby mój skrypt bash spał do określonego czasu. Tak więc chcę polecenie typu „sen”, które nie zajmuje przerwy, ale kończy czas i śpi do tego czasu.

Demon „at” nie jest rozwiązaniem, ponieważ muszę blokować uruchomiony skrypt do określonej daty / godziny.

Czy jest takie polecenie?

theomega
źródło
1
Zwróć uwagę, że rozwiązanie, które po prostu wykorzystuje długi sen poprzedzony obliczeniami, może spać zbyt długo, może o wiele za długo, zwłaszcza jeśli masz maszynę, która może przejść w stan hibernacji. Komenda posix sleep nie obiecuje, że nie będziesz spać zbyt długo. Rozwiązanie autorstwa @Camusensei bardzo ładnie rozwiązuje ten problem.
GregD

Odpowiedzi:

98

Jak wspomniał Outlaw Programmer, myślę, że rozwiązaniem jest po prostu spanie przez odpowiednią liczbę sekund.

Aby to zrobić w bashu, wykonaj następujące czynności:

current_epoch=$(date +%s)
target_epoch=$(date -d '01/01/2010 12:00' +%s)

sleep_seconds=$(( $target_epoch - $current_epoch ))

sleep $sleep_seconds

Aby dodać precyzję do nanosekund (efektywniej około milisekund), użyj np. Następującej składni:

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

Zauważ, że macOS / OS X nie obsługuje precyzji poniżej sekund, zamiast tego musisz użyć coreutilsfrom brewzobacz te instrukcje

SpoonMeiser
źródło
8
Dzięki za to napisałem mały skrypt powłoki, aby uzyskać polecenie „sleepuntil”.
theomega
1
@enigmaticPhysicist: istnieje podobne wywołanie polecenia „at”, które działa asynchronicznie.
voidlogic
6
Jeśli chcesz zatrzymać się jutro o 3 nad ranem, możesz użyć target_epoch=$(date -d 'tomorrow 03:00' +%s)zamiast tego.
Yajo
Możesz zrobić to samo, używając jednego widelca datezamiast dwóch! Zobacz pierwszą część mojej odpowiedzi
F. Hauri
1
Zauważ, że to rozwiązanie może potencjalnie spać zbyt długo, a jeśli twój komputer trochę hibernuje, może to być znacznie za długie.
GregD
23

Ponieważ to pytanie zadano 4 lata temu, ta pierwsza część dotyczy starych wersji basha:

Ostatnia edycja: środa, 22 kwietnia 2020 r., Między 10:30 a 10: 55 (ważne przy czytaniu próbek)

Metoda ogólna (unikaj bezużytecznych wideł!)

(Uwaga: ta metoda date -fnie jest POSIX i nie działa pod MacOS! Jeśli pod Mac, przejdź do mojego czystegofunkcja )

Aby zmniejszyć forks, zamiast biegać datedwa razy, wolę używać tego:

Prosta próbka początkowa

sleep $(($(date -f - +%s- <<< $'tomorrow 21:30\nnow')0))

gdzie w przyszłości tomorrow 21:30może zostać zastąpiona dowolną datą i formatem rozpoznawanym przez date.

Z wysoką precyzją (nanosec)

Prawie to samo:

sleep $(bc <<<s$(date -f - +'t=%s.%N;' <<<$'07:00 tomorrow\nnow')'st-t')

Sięgam następnym razem

Aby osiągnąć następnyHH:MM sens dzisiaj, jeśli to możliwe, jutro, jeśli jest za późno:

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

To działa pod , i inne nowoczesne muszle, ale musisz użyć:

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

pod lżejszymi muszlami, jak lub .

Czysty sposób, bez widelca !!

Przetestowane pod MacOS!

Napisałem jeden dwa małe funkcje: sleepUntilisleepUntilHires

 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)

Ponieważ nowe wersje basha oferują printfopcję pobierania daty, dla tego nowego sposobu spania do GG: MM bez użycia datelub jakiegokolwiek innego widelca, zbudowałem trochęfunkcjonować. Oto ona:

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
}

Następnie:

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

Jeśli cel jest wcześniej, będzie spał do jutra:

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

Czas HiRes z pod GNU / Linux

Niedawny , od wersji 5.0 dodaje nową $EPOCHREALTIMEzmienną z mikrosekundami. Z tego wynika sleepUntilHiresfunkcja.

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
}

Uwaga: to użycie, read -tktóre jest wbudowane, zamiast sleep. Niestety, to nie zadziała, gdy działa w tle, bez prawdziwego TTY. Zapraszam do zastąpienia read -tprzez sleepjeśli masz zamiar uruchomić to w skryptach tle ... (Ale za proces w tle, należy rozważyć użycie croni / lub atzamiast to wszystko)

Pomiń następny akapit, aby zobaczyć testy i ostrzeżenie $ËPOCHSECONDS!

Najnowsze jądro unika używania /proc/timer_listprzez użytkownika !!

W ostatnim jądrze Linuksa znajdziesz plik zmiennych o nazwie `/ proc / timer_list`, w którym możesz odczytać` offset` i `now` zmienną, w ciągu ** nanosekund **. Możemy więc obliczyć czas snu, aby osiągnąć * najwyższy * pożądany czas.

(Napisałem to, aby generować i śledzić określone zdarzenia w bardzo dużych plikach dziennika, zawierających tysiące wierszy przez jedną sekundę).

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
}

Po zdefiniowaniu dwóch zmiennych tylko do odczytuTIMER_LIST_OFFSET i TIMER_LIST_SKIP, funkcja bardzo szybko uzyska dostęp do pliku zmiennej w /proc/timer_listcelu obliczenia czasu uśpienia:

Mała funkcja testowa

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
}

Może renderować coś takiego:

sleep 0.244040s, -> Wed Apr 22 10:34:39 2020
2020-04-22-10:34:39.001685312
2020-04-22-10:34:39.922291769
sleep 0.077012s, -> Wed Apr 22 10:34:40 2020
2020-04-22-10:34:40.004264869
  • Na początku następnej sekundy
  • czas drukowania
  • poczekaj 0,92 sekundy
  • czas drukowania
  • oblicz pozostało 0,07 sekundy do następnej sekundy
  • spać 0,07 sekundy
  • czas druku.

Uważaj, aby nie mieszać $EPOCHSECONDi $EPOCHREALTIME!

Przeczytaj moje ostrzeżenie dotyczące różnicy między $EPOCHSECONDa$EPOCHREALTIME

Ta funkcja jest używana, $EPOCHREALTIMEwięc nie używaj $EPOCHSECONDdo ustalenia następnej sekundy :

Przykładowy problem: Następna próba wydrukowania czasu zaokrąglonego o 2 sekundy:

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

Może produkować:

sleep 0.587936s, -> Wed Apr 22 10:51:26 2020
Wed Apr 22 10:51:26 2020.000630
sleep 86399.998797s, -> Thu Apr 23 10:51:26 2020
^C
F. Hauri
źródło
1
Łał! Obliczanie nanosekund pod bash !
F. Hauri,
Obliczanie mikro sekund pod natywnym bash v5 + !!
F. Hauri
21

Użyj sleep, ale oblicz czas za pomocą date. Będziesz chciał tego użyć date -d. Załóżmy na przykład, że chcesz poczekać do następnego tygodnia:

expr `date -d "next week" +%s` - `date -d "now" +%s`

Po prostu zastąp „następny tydzień” dowolną datą, na którą chcesz czekać, a następnie przypisz to wyrażenie do wartości i śpij przez tyle sekund:

startTime=$(date +%s)
endTime=$(date -d "next week" +%s)
timeToWait=$(($endTime- $startTime))
sleep $timeToWait

Gotowe!

John Feminella
źródło
Warto rozwinąć sposób przypisywania wartości do zmiennej, ponieważ timeToWait = expr ... nie będzie działać bezpośrednio i nie możesz używać lewych apostrofów, ponieważ nie zagnieżdżają się, więc będziesz musiał użyć $ () lub zmienne tymczasowe.
SpoonMeiser
Dobry pomysł; Zmienię moje, aby było jaśniejsze, aby ludzie nie zrozumieli złego pomysłu.
John Feminella,
Uwaga -djest jak dotąd rozszerzeniem spoza POSIX. We FreeBSD próbuje ustawić wartość jądra na czas letni.
Dave C
9

Oto rozwiązanie, które wykonuje zadanie ORAZ informuje użytkownika o pozostałym czasie. Używam go prawie codziennie do uruchamiania skryptów w nocy (używając cygwin, ponieważ nie mogłem dostać się crondo pracy na Windowsie)

cechy

  • Precyzja co do sekundy
  • Wykrywa zmiany czasu systemu i dostosowuje się
  • Inteligentne wyjście informujące, ile czasu pozostało
  • 24-godzinny format wprowadzania
  • zwraca true, aby móc łączyć się z &&

Próbny przebieg

$ 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

(Data na końcu nie jest częścią funkcji, ale ze względu na && date)

Kod

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
}
Camusensei
źródło
8

Możesz zatrzymać wykonywanie procesu, wysyłając mu sygnał SIGSTOP, a następnie wznowić wykonywanie, wysyłając mu sygnał SIGCONT.

Możesz więc zatrzymać skrypt, wysyłając SIGSTOP:

kill -SIGSTOP <pid>

A następnie użyj at demona, aby go obudzić, wysyłając SIGCONT w ten sam sposób.

Prawdopodobnie twój skrypt poinformuje o tym, kiedy chciał zostać obudzony przed zaśnięciem.

SpoonMeiser
źródło
8

W Ubuntu 12.04.4 LTS tutaj jest proste wejście bash, które działa:

sleep $(expr `date -d "03/21/2014 12:30" +%s` - `date +%s`)
Anthony O.
źródło
7

Aby kontynuować odpowiedź SpoonMeisera, oto konkretny przykład:

$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
$
Wstrzymano do odwołania.
źródło
Myślę, że chcesz zaplanować SIGCONT, a nie inny SIGSTOP, więc albo sygnał, albo komentarz są nieprawidłowe. W przeciwnym razie miło zobaczyć odpowiedni przykład.
SpoonMeiser
Ups, wpisałem komentarz. Teraz naprawione. Ale musisz oglądać za pomocą sygnałów numerycznych, ponieważ mogą być różne.
Wstrzymano do odwołania.
Okazało się, że myślnik wydaje się nie rozumieć SIGCONT, więc na początku użyłem -18, który jest nieprzenośny, potem stwierdziłem, że lubi CONT, więc zredagowałem powyższą odpowiedź, aby to naprawić.
Wstrzymano do odwołania.
6

Chciałem skryptu, który sprawdzałby tylko godziny i minuty, abym mógł codziennie uruchamiać skrypt z tymi samymi parametrami. Nie chcę się martwić, który dzień będzie jutro. Więc zastosowałem inne podejście.

target="$1.$2"
cur=$(date '+%H.%M')
while test $target != $cur; do
    sleep 59
    cur=$(date '+%H.%M')
done

parametrami skryptu są godziny i minuty, więc mogę napisać coś takiego:

til 7 45 && mplayer song.ogg

(aż to nazwa skryptu)

nie ma więcej dni spóźnienia w pracy, ponieważ wpisałeś dzień. Twoje zdrowie!

kolomer
źródło
4

timeToWait = $ (($ end - $ start))

Uważaj, że „timeToWait” może być liczbą ujemną! (na przykład, jeśli ustawisz tryb uśpienia do „15:57”, a teraz jest „15:58”). Musisz więc to sprawdzić, aby uniknąć dziwnych błędów wiadomości:

#!/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
Ariel
źródło
Jednak dobre rozwiązanie, myślę, że $ SECONDS jest ciągiem znaków, więc kod obronny powinien być taki, jak najpierw uzyskać podciąg, SIGNCHAR=${SECONDS:0:1}a potem if [ "$SIGNCHAR" = "-" ]. Że należy to zrobić.
H2ONaCl
1

Możesz obliczyć liczbę sekund między teraz a czasem budzenia i użyć istniejącego polecenia „uśpienia”.

Wyjęty spod prawa programista
źródło
1

Możesz użyć „at”, aby wysłać sygnał do twojego skryptu, który czekał na ten sygnał.

Kim Reece
źródło
Człowieku, właśnie pisałem kolejną odpowiedź w tej sprawie.
SpoonMeiser
Cóż, właściwie nie, mój był nieco inny.
SpoonMeiser
0

Oto coś, co właśnie napisałem, aby zsynchronizować wielu klientów testowych:

#!/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)

Ten skrypt jest w stanie uśpienia do następnego „zaokrąglonego” lub „ostrego” czasu.

Prostym przypadkiem użycia jest uruchomienie ./sleep.py 10; ./test_client1.pyw jednym terminalu i ./sleep.py 10; ./test_client2.pyinnym.

Dima Tisnek
źródło
Gdzie jest związek z moim pytaniem?
theomega
łatwo go rozszerzyć, aby untilustawić konkretną datę / godzinę
Dima Tisnek
0
 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
Nick Constantine
źródło
Czy możesz dodać jakiś opis do tego rozwiązania?
dominacja
to jest skrypt bash; funkcja sleepuntil przyjmuje jeden argument; ustawiasz zmienną target_time = "11:59" lub jakikolwiek czas, do którego chcesz spać; następnie wywołujesz funkcję z argumentem target_time. Oblicza, ile sekund pozostało do tego docelowego czasu i śpi przez tę liczbę sekund
Nick Constantine
0

W tym celu stworzyłem małe narzędzie o nazwie Hypnos . Do tego czasu jest konfigurowany przy użyciu składni crontab i bloków.

#!/bin/bash
while [ 1 ]; do
  hypnos "0 * * * *"
  echo "running some tasks..."
  # ...
done
Kris Foster
źródło
0

Aby rozszerzyć główną odpowiedź, oto kilka ważnych przykładów dotyczących manipulacji ciągiem daty:

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
NVRM
źródło
-1

W OpenBSD , można użyć następującego rozwiązania, aby skompresować */55-minutową crontab(5)pracę do jednej 00godzinnej (aby mieć pewność, że generowanych jest mniej wiadomości e-mail, a wszystko to podczas wykonywania tego samego zadania w dokładnych odstępach czasu):

#!/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

Zauważ, że złamałoby date(1)to również sleep(1)projekt w ostatniej iteracji, ponieważ 60minuty nie są prawidłowym czasem (chyba że tak jest!), Więc nie będziemy musieli czekać dłużej przed otrzymaniem naszego raportu e-mail.

Należy również pamiętać, że jeśli jedna z iteracji zajmie więcej niż 5 minut, plik sleep również łaskawie zawiodłaby z założenia, ponieważ w ogóle nie spałaby (z powodu liczby ujemnej interpretowanej jako opcja wiersza poleceń, zamiast zawijania do następnej godziny lub nawet wieczności), upewniając się w ten sposób, że Twoja praca może zostać ukończona w ciągu wyznaczonej godziny (np. jeśli tylko jedna z iteracji zajmie trochę więcej niż 5 minut, nadal będziemy mieli czas, aby nadrobić zaległości, bez wszystko, co zawija się do następnej godziny).

printf(1)Jest potrzebna, ponieważ dateoczekuje dokładnie dwie cyfry specyfikacji minut.

cnst
źródło
-2

Użyj smoły. To narzędzie, które napisałem, aby to zrobić.

https://github.com/metaphyze/tarry/

To proste narzędzie wiersza poleceń służące do czekania do określonej godziny. To nie to samo, co „sen”, który będzie czekał przez pewien czas. Jest to przydatne, jeśli chcesz wykonać coś w określonym czasie lub, co bardziej prawdopodobne, wykonać kilka rzeczy dokładnie w tym samym czasie, na przykład sprawdzić, czy serwer może obsłużyć wiele bardzo jednoczesnych żądań. Możesz go użyć w ten sposób z „&&” w systemie Linux, Mac lub Windows:

   tarry -until=16:03:04 && someOtherCommand

To czekałoby do 16:03:04, a następnie wykonywałoby niektóreOtherCommand. Oto przykład dla systemu Linux / Mac pokazujący, jak uruchomić wiele żądań zaplanowanych do jednoczesnego uruchomienia:

   for request in 1 2 3 4 5 6 7 8 9 10
   do
       tarry -until=16:03:04 && date > results.$request &
   done

Pliki binarne Ubuntu, Linux i Windows są dostępne za pośrednictwem łączy na stronie.

metafiza
źródło