Je veux que mon script bash s'endorme jusqu'à une heure précise. Donc, je veux une commande comme "sleep" qui ne prend aucun intervalle mais une heure de fin et dort jusque-là.
Le démon "at" n'est pas une solution, car je dois bloquer un script en cours d'exécution jusqu'à une certaine date / heure.
Existe-t-il une telle commande?
Réponses:
Comme mentionné par Outlaw Programmer, je pense que la solution est simplement de dormir pendant le bon nombre de secondes.
Pour ce faire dans bash, procédez comme suit:
current_epoch=$(date +%s) target_epoch=$(date -d '01/01/2010 12:00' +%s) sleep_seconds=$(( $target_epoch - $current_epoch )) sleep $sleep_seconds
Pour ajouter de la précision jusqu'à nanosecondes (effectivement plus d'environ millisecondes), utilisez par exemple cette syntaxe:
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
Notez que macOS / OS X ne prend pas en charge la précision inférieure à quelques secondes, vous devrez utiliser
coreutils
à labrew
place → voir ces instructionsla source
target_epoch=$(date -d 'tomorrow 03:00' +%s)
place.date
lieu de deux! Voir 1ère partie de ma réponseComme cette question a été posée il y a 4 ans, cette première partie concerne les anciennes versions de bash:
Dernière édition: mercredi 22 avril 2020, quelque chose entre 10h30 et 10h: 55 (Important pour la lecture d'échantillons)
Méthode générale (évitez les fourches inutiles!)
(Nota: cette méthode utilise
date -f
qui n'est pas POSIX et ne fonctionne pas sous MacOS! Si sous Mac, allez à mon purfrapperfonction )Afin de réduire
forks
, au lieu de courirdate
deux fois, je préfère utiliser ceci:Échantillon de départ simple
sleep $(($(date -f - +%s- <<< $'tomorrow 21:30\nnow')0))
où
tomorrow 21:30
pourrait être remplacé par tout type de date et de format reconnu pardate
, à l'avenir.Avec haute précision (nanosec)
Presque pareil:
sleep $(bc <<<s$(date -f - +'t=%s.%N;' <<<$'07:00 tomorrow\nnow')'st-t')
Atteindre prochaine fois
Pour atteindre le prochain
HH:MM
sens aujourd'hui si possible, demain si trop tard:sleep $((($(date -f - +%s- <<<$'21:30 tomorrow\nnow')0)%86400))
Cela fonctionne sous frapper, ksh et d'autres shells modernes, mais vous devez utiliser:
sleep $(( ( $(printf 'tomorrow 21:30\nnow\n' | date -f - +%s-)0 )%86400 ))
sous plus léger coques commecendre ou tiret.
Pur frapper façon, pas de fourchette !!
Testé sous MacOS!
J'ai écrit
unedeux petites fonctions:sleepUntil
etsleepUntilHires
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)
Comme les nouvelles versions de bash offrent une
printf
option pour récupérer la date, pour cette nouvelle façon de dormir jusqu'à HH: MM sans utiliserdate
ou tout autre fork, j'ai construit un peufrapperfonction. C'est ici: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 }
Ensuite:
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
Si l'objectif est avant, cela dormira jusqu'à demain:
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
Temps HiRes avec frapper sous GNU / Linux
Récent frapper, à partir de la version 5.0, ajoutez une nouvelle
$EPOCHREALTIME
variable en microsecondes. De là, il y a unesleepUntilHires
fonction.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 }
Remarque: cette utilisation
read -t
est intégrée à la place desleep
. Malheureusement, cela ne fonctionnera pas lors de l'exécution en arrière-plan, sans vrai TTY. N'hésitez pas à remplacerread -t
parsleep
si vous prévoyez d'exécuter ceci dans des scripts d'arrière-plan ... (Mais pour le processus d'arrière-plan, envisagez d'utilisercron
et / ou à laat
place de tout cela)Passer le paragraphe suivant pour les tests et les avertissements sur
$ËPOCHSECONDS
!Le noyau récent évite d'utiliser
/proc/timer_list
par l'utilisateur !!Sous le noyau Linux récent, vous trouverez un fichier de variables nommé `/ proc / timer_list` où vous pourrez lire un` offset` et une variable `now`, en ** nanosecondes **. Nous pouvons donc calculer le temps de sommeil pour atteindre le temps * très haut * souhaité.(J'ai écrit ceci pour générer et suivre des événements spécifiques sur de très gros fichiers journaux, contenant mille lignes pendant une seconde).
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 }
Après avoir défini deux variables en lecture seule ,
TIMER_LIST_OFFSET
etTIMER_LIST_SKIP
, la fonction accédera très rapidement au fichier de variables/proc/timer_list
pour le calcul du temps de sommeil:Petite fonction de test
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 }
Peut rendre quelque chose comme:
Attention à ne pas mélanger
$EPOCHSECOND
et$EPOCHREALTIME
!Lisez mon avertissement sur la différence entre
$EPOCHSECOND
et$EPOCHREALTIME
Cette fonction est utilisée,
$EPOCHREALTIME
donc ne l'utilisez pas$EPOCHSECOND
pour établir la seconde suivante :Exemple de problème: tentative d'impression de l'heure suivante arrondie de 2 secondes:
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
Peut produire:
la source
Utilisez
sleep
, mais calculez le temps en utilisantdate
. Vous voudrez l'utiliserdate -d
pour cela. Par exemple, disons que vous vouliez attendre la semaine prochaine:expr `date -d "next week" +%s` - `date -d "now" +%s`
Remplacez simplement "la semaine prochaine" par la date que vous souhaitez attendre, puis attribuez cette expression à une valeur et dormez pendant autant de secondes:
startTime=$(date +%s) endTime=$(date -d "next week" +%s) timeToWait=$(($endTime- $startTime)) sleep $timeToWait
Terminé!
la source
-d
est une extension non POSIX à ce jour. Sur FreeBSD, il essaie de définir la valeur du noyau pour l'heure d'été.Voici une solution qui fait le travail ET informe l'utilisateur du temps restant. Je l'utilise presque tous les jours pour exécuter des scripts pendant la nuit (en utilisant cygwin, car je ne pouvais pas
cron
travailler sous Windows)traits
&&
Exemple d'exécution
(La date à la fin ne fait pas partie de la fonction, mais en raison de la
&& date
)Code
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 }
la source
Vous pouvez arrêter l'exécution d'un processus en lui envoyant un signal SIGSTOP, puis le faire reprendre l'exécution en lui envoyant un signal SIGCONT.
Vous pouvez donc arrêter votre script en envoyant un SIGSTOP:
kill -SIGSTOP <pid>
Et puis utilisez le at deamon pour le réveiller en lui envoyant un SIGCONT de la même manière.
Vraisemblablement, votre script vous informera du moment où il a voulu être réveillé avant de s'endormir.
la source
Sur Ubuntu 12.04.4 LTS, voici la simple entrée bash qui fonctionne:
sleep $(expr `date -d "03/21/2014 12:30" +%s` - `date +%s`)
la source
Pour suivre la réponse de SpoonMeiser, voici un exemple spécifique:
$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 $
la source
Je voulais un script qui ne vérifiait que les heures et les minutes afin de pouvoir exécuter le script avec les mêmes paramètres chaque jour. Je ne veux pas m'inquiéter de quel jour sera demain. J'ai donc utilisé une approche différente.
target="$1.$2" cur=$(date '+%H.%M') while test $target != $cur; do sleep 59 cur=$(date '+%H.%M') done
les paramètres du script sont les heures et les minutes, donc je peux écrire quelque chose comme:
(til est le nom du script)
plus de jours de retard au travail parce que vous avez mal saisi la journée. à votre santé!
la source
Attention, "timeToWait" pourrait être un nombre négatif! (par exemple, si vous spécifiez de dormir jusqu'à "15:57" et maintenant c'est "15:58"). Vous devez donc le vérifier pour éviter des erreurs de message étranges:
#!/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
la source
SIGNCHAR=${SECONDS:0:1}
et ensuiteif [ "$SIGNCHAR" = "-" ]
. Ça devrait le faire.Vous pouvez calculer le nombre de secondes entre maintenant et l'heure de réveil et utiliser la commande 'sleep' existante.
la source
Vous pourriez peut-être utiliser «at» pour envoyer un signal à votre script, qui attendait ce signal.
la source
J'ai en fait écrit https://tamentis.com/projects/sleepuntil/ dans ce but précis. C'est un peu sur-tuer la plupart du code vient de BSD 'at' donc il est assez conforme aux normes:
la source
Voici quelque chose que j'ai écrit tout à l'heure pour synchroniser plusieurs clients de test:
#!/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)
Ce script dort jusqu'au prochain moment "arrondi" ou "pointu".
Un cas d'utilisation simple consiste à exécuter
./sleep.py 10; ./test_client1.py
dans un terminal et./sleep.py 10; ./test_client2.py
dans un autre.la source
until
soit réglé à une date / heure spécifiquefunction 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
la source
J'ai mis en place un petit utilitaire appelé Hypnos pour ce faire. Il est configuré à l'aide de la syntaxe crontab et des blocs jusqu'à ce moment-là.
#!/bin/bash while [ 1 ]; do hypnos "0 * * * *" echo "running some tasks..." # ... done
la source
Pour étendre la réponse principale, voici quelques exemples valides concernant la manipulation de la chaîne de date:
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
la source
Sur OpenBSD , les éléments suivants peuvent être utilisés pour compacter un travail de
*/5
5 minutescrontab(5)
en un travail00
horaire (pour s'assurer que moins d'e-mails sont générés, tout en effectuant la même tâche à des intervalles exacts):#!/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
Notez que
date(1)
cela briserait également lasleep(1)
conception lors de l'itération finale, car les60
minutes ne sont pas une heure valide (sauf si c'est le cas!), Nous n'aurons donc pas à attendre plus longtemps avant de recevoir notre rapport par courrier électronique.Notez également que si l'une des itérations prend plus de 5 minutes qui lui sont allouées, le
sleep
cela échouerait également gracieusement par conception en ne dormant pas du tout (en raison de ce qui est un nombre négatif interprété comme une option de ligne de commande, au lieu de revenir à l'heure suivante ou même l'éternité), garantissant ainsi que votre travail puisse encore se terminer dans l'heure allouée (par exemple, si une seule des itérations prend un peu plus de 5 minutes, nous aurions encore le temps de rattraper le retard, sans quoi que ce soit qui s'enroule jusqu'à l'heure suivante).Le
printf(1)
est nécessaire cardate
attend exactement deux chiffres pour la spécification des minutes.la source
Utilisez du temps. C'est un outil que j'ai écrit pour le faire spécifiquement.
https://github.com/metaphyze/tarry/
Il s'agit d'un simple outil de ligne de commande pour attendre un moment précis. Ce n'est pas la même chose que «dormir» qui attendra un certain temps. Ceci est utile si vous voulez exécuter quelque chose à un moment précis ou plus probablement exécuter plusieurs choses exactement en même temps, comme tester si un serveur peut gérer plusieurs requêtes très simultanées. Vous pouvez l'utiliser comme ceci avec "&&" sous Linux, Mac ou Windows:
Cela attendrait jusqu'à 4:03:04 PM, puis exécuterait someOtherCommand. Voici un exemple Linux / Mac de la façon d'exécuter plusieurs requêtes planifiées pour démarrer en même temps:
for request in 1 2 3 4 5 6 7 8 9 10 do tarry -until=16:03:04 && date > results.$request & done
Les binaires Ubuntu, Linux et Windows sont disponibles via des liens sur la page.
la source