Como essa pergunta foi feita há 4 anos, a primeira parte se refere a versões antigas do bash:
Última edição: Quarta-feira, 22 de abril de 2020, algo entre 10h30 e 10h55 (importante para ler amostras)
Método geral (evite garfos inúteis!)
(Nota: este método não usa date -f
POSIX e não funciona no MacOS! Se for no Mac, vá para o meu purobashfunção )
Para reduzir forks
, em vez de correr date
duas vezes, prefiro usar isto:
Amostra inicial simples
sleep $(($(date -f - +%s- <<< $'tomorrow 21:30\nnow')0))
onde tomorrow 21:30
poderia ser substituído por qualquer tipo de data e formato reconhecido por date
, no futuro.
Com alta precisão (nanosec)
Quase o mesmo:
sleep $(bc <<<s$(date -f - +'t=%s.%N;' <<<$'07:00 tomorrow\nnow')'st-t')
Alcançando a próxima vez
Para alcançar o próximoHH:MM
significado hoje, se possível, amanhã se for tarde demais:
sleep $((($(date -f - +%s- <<<$'21:30 tomorrow\nnow')0)%86400))
Isso funciona sob bash, ksh e outros shells modernos, mas você tem que usar:
sleep $(( ( $(printf 'tomorrow 21:30\nnow\n' | date -f - +%s-)0 )%86400 ))
sob conchas mais leves comocinza ou traço.
Puro bash caminho, sem garfo !!
Testado em MacOS!
Eu escrevi um duas funções pequenos: sleepUntil
esleepUntilHires
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)
Como as novas versões do bash oferecem uma printf
opção para recuperar a data, para esta nova maneira de dormir até HH: MM sem usar date
ou qualquer outro fork, eu construí um poucobashfunção. Aqui está:
sleepUntil() {
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
}
Então:
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
Se o alvo estiver antes, isso dormirá até amanhã:
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
Tempo de HiRes com bash sob GNU / Linux
Recente bash, da versão 5.0 adiciona uma nova $EPOCHREALTIME
variável com microssegundos. Disto existe uma sleepUntilHires
função.
sleepUntilHires () {
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
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
}
Atenção: este uso read -t
é embutido, em vez de sleep
. Infelizmente, isso não funcionará quando executado em segundo plano, sem TTY real. Sinta-se à vontade para substituir read -t
por sleep
se você planeja executar isso em scripts de segundo plano ... (mas para o processo de segundo plano, considere usar cron
e / ou em at
vez de tudo isso)
Pule o próximo parágrafo para testes e avisos sobre $ËPOCHSECONDS
!
O kernel recente evita o uso /proc/timer_list
pelo usuário !!
No kernel Linux recente, você encontrará um arquivo de variáveis chamado `/ proc / timer_list` onde você pode ler um` offset` e uma variável `now`, em ** nanossegundos **. Portanto, podemos calcular o tempo de sono para chegar ao * máximo * tempo desejado.
(Eu escrevi isso para gerar e rastrear eventos específicos em arquivos de log muito grandes, contendo mil linhas por um 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
}
Depois de definir duas variáveis somente leitura , TIMER_LIST_OFFSET
e TIMER_LIST_SKIP
, a função acessará muito rapidamente o arquivo de variável /proc/timer_list
para calcular o tempo de sono:
Pequena função de teste
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
}
Pode renderizar algo como:
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
- No início do próximo segundo,
- tempo de impressão, então
- espere 0,92 segundo, então
- tempo de impressão, então
- computar 0,07 segundos restantes, para o próximo segundo
- dormir 0,07 segundos, então
- tempo de impressão.
Cuidado para não misturar $EPOCHSECOND
e $EPOCHREALTIME
!
Leia meu aviso sobre a diferença entre $EPOCHSECOND
e$EPOCHREALTIME
Esta função usa, $EPOCHREALTIME
portanto, não use $EPOCHSECOND
para estabelecer o próximo segundo :
Problema de amostra: tentar imprimir a próxima hora arredondada em 2 segundos:
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
Pode produzir:
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