while second funcion example bash sleep wait

bash - second - sleep linux command



Bash: Dormir hasta una hora/fecha especĂ­fica (16)

Quiero que mi script bash duerma hasta un momento específico. Por lo tanto, quiero un comando como "dormir", que no requiere intervalo sino un tiempo de finalización y duerme hasta entonces.

El "at" -daemon no es una solución, ya que necesito bloquear un script en ejecución hasta una fecha / hora determinada.

¿Hay tal comando?


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

¡Cuidado con que "timeToWait" podría ser un número negativo! (por ejemplo, si especifica dormir hasta "15:57" y ahora es "15:58"). Por lo tanto, debe verificarlo para evitar errores de mensajes extraños:

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


Aquí hay algo que escribí justo ahora para sincronizar múltiples clientes de prueba:

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

Este script duerme hasta el próximo tiempo "redondeado" o "agudo".

Un caso de uso simple es ejecutar ./sleep.py 10; ./test_client1.py ./sleep.py 10; ./test_client1.py en un terminal y ./sleep.py 10; ./test_client2.py ./sleep.py 10; ./test_client2.py en otro.


Aquí hay una solución que hace el trabajo Y le informa al usuario sobre cuánto tiempo le queda. Lo uso casi todos los días para ejecutar scripts durante la noche (usando cygwin, ya que no pude hacer que cron funcione en Windows)

Caracteristicas

  • Preciso hasta el segundo
  • Detecta cambios en el tiempo del sistema y se adapta
  • Salida inteligente que indica cuánto tiempo queda
  • Formato de entrada de 24 horas
  • devuelve verdadero para poder encadenar con &&

Ejecución de muestra

$ 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

(La fecha al final no es parte de la función, pero debido a la && date )

Código

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 }


Arreglé una pequeña utilidad llamada Hypnos para hacer esto. Se configura utilizando la sintaxis crontab y los bloques hasta ese momento.

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


Como lo menciona Outlaw Programmer, creo que la solución es solo dormir la cantidad correcta de segundos.

Para hacer esto en bash, haz lo siguiente:

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

Para agregar precisión a nanosegundos (efectivamente más alrededor de milisegundos) use, por ejemplo, esta sintaxis:

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

Tenga en cuenta que macOS / OS X no admite precisión por debajo de segundos, en su lugar necesitaría usar coreutils de brewvea estas instrucciones


Como se hizo esta pregunta hace 4 años, esta primera parte se refiere a las versiones anteriores de bash:

Método general

Para reducir las forks , en lugar de ejecutar la date dos veces, prefiero usar esto:

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

donde tomorrow 21:30 podría ser reemplazado por cualquier tipo de fecha y formato reconocidos por date , en el futuro.

o mejor, para llegar a la siguiente HH:MM significando hoy si es posible, mañana si es demasiado tarde:

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

Esto funciona bajo bash , ksh y otras conchas modernas, pero debes usar:

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

bajo conchas más ligeras como ash o dash .

Nueva forma de bash (sin tenedor)

Como mi necesidad de este tipo de herramienta nunca ha superado las 24 horas, lo siguiente solo se referirá a la sintaxis de HH , HH:MM o HH:MM:SS en las próximas 24 horas . (De todos modos, si necesita más, incluso podría volver al método anterior utilizando un tenedor hasta la date . Intentar eliminar un tenedor en un script que se ejecuta durante muchos días es excesivo.)

Como las nuevas versiones de bash ofrecen una opción printf para recuperar la fecha, para esta nueva forma de dormir hasta HH: MM sin usar date u otro fork, he creado una pequeña función bash . Aquí está:

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 }

Entonces:

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

Tiempo de HiRes con bash bajo GNU / Linux

Bajo el reciente kernel de Linux, encontrará un archivo de variables llamado /proc/timer_list donde podría leer un offset y una variable now , en nanosegundos . Entonces podemos calcular el tiempo de sueño para alcanzar el tiempo deseado más alto .

(Escribí esto para generar y rastrear eventos específicos en archivos de registro muy grandes, que contienen mil líneas por un segundo).

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 }

Después de definir dos variables de solo lectura , TIMER_LIST_OFFSET y TIMER_LIST_SKIP , la función accederá muy rápidamente al archivo variable /proc/timer_list para calcular el tiempo de /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

Y finalmente

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

Donde, por 1 minuto completo , hay:

60sec - .97sec (reposo) - 59.024430416sec (reposo) = .005569584

Hay menos de 1/100 de segundo para hacer una horquilla hasta la date , leer temporizadores y calcular el tiempo de reposo.

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


De hecho, escribí https://tamentis.com/projects/sleepuntil/ para este propósito exacto. Es un poco exagerado: la mayoría del código proviene de BSD ''at'', por lo que es bastante compatible con las normas:

$ sleepuntil noon && sendmail something


En OpenBSD , lo siguiente podría usarse para compactar un trabajo */5 cinco minutos crontab(5) en uno 00 cada hora (para asegurarse de que se generan menos correos electrónicos, todo mientras se realiza la misma tarea a intervalos exactos):

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

Tenga en cuenta que la date(1) también rompería la sleep(1) por diseño en la iteración final, ya que 60 minutos no es una hora válida (¡a menos que lo sea!), Por lo que no tendremos que esperar ningún tiempo adicional antes de obteniendo nuestro informe de correo electrónico.

También tenga en cuenta que si una de las iteraciones tarda más de 5 minutos asignados a ella, el sleep fracasaría por diseño al no poder dormir (debido a que un número negativo se interpreta como una opción de línea de comando, en lugar de envolverlo). a la siguiente hora o incluso a la eternidad), asegurándose así de que su trabajo aún pueda completarse dentro de la hora asignada (por ejemplo, si solo una de las iteraciones tarda un poco más de 5 minutos, entonces todavía tendríamos el tiempo para ponernos al día, sin nada envolviendo la próxima hora).

El printf(1) es necesario porque la date espera exactamente dos dígitos para la especificación de minutos.


En Ubuntu 12.04.4 LTS aquí está la entrada bash simple que funciona:

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


Para seguir la respuesta de SpoonMeiser, aquí hay un ejemplo específico:

$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 $


Puede calcular el número de segundos entre ahora y el tiempo de activación y usar el comando existente ''dormir''.


Puede detener la ejecución de un proceso, enviándole una señal SIGSTOP, y luego hacer que reanude la ejecución enviándole una señal SIGCONT.

Para que pueda detener su script al enviar es un SIGSTOP:

kill -SIGSTOP <pid>

Y luego use at at deamon para reactivarlo enviándole un SIGCONT de la misma manera.

Presumiblemente, su script informará cuándo quiso ser despertado antes de ponerse a dormir.


Quería un script que solo verificara las horas y los minutos para poder ejecutar el script con los mismos parámetros todos los días. No quiero preocuparme sobre qué día será mañana. Entonces usé un enfoque diferente.

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

los parámetros del script son las horas y los minutos, así que puedo escribir algo como:

til 7 45 && mplayer song.ogg

(hasta que es el nombre del script)

no más días tarde en el trabajo porque te equivocaste en el día. ¡aclamaciones!


Tal vez podrías usar ''at'' para enviar una señal a tu script, que estaba esperando esa señal.


Use sleep , pero calcule el tiempo usando date . Querrá usar la date -d para esto. Por ejemplo, supongamos que quieres esperar hasta la próxima semana:

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

Solo sustituya "la próxima semana" con la fecha que desee esperar, luego asigne esta expresión a un valor y espere tantos segundos:

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

¡Todo listo!


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