Estou executando esse loop para verificar e imprimir algumas coisas a cada segundo. No entanto, como os cálculos demoram talvez algumas centenas de milissegundos, o tempo impresso às vezes pula um segundo.
Existe alguma maneira de escrever um loop que eu garanta uma impressão a cada segundo? (Desde que, é claro, que os cálculos no loop demorem menos de um segundo :))
while true; do
TIME=$(date +%H:%M:%S)
# some calculations which take a few hundred milliseconds
FOO=...
BAR=...
printf '%s %s %s\n' $TIME $FOO $BAR
sleep 1
done
bash
timestamps
sleep
fortrina
fonte
fonte
sched(7)
API (POSIX: consulte<sched.h>
e páginas vinculadas a partir daí), você basicamente não pode ter garantias em tempo real deste formulário.Respostas:
Para ficar um pouco mais próximo do código original, o que faço é:
Isso muda um pouco a semântica: se suas coisas levarem menos de um segundo, simplesmente esperará o segundo inteiro passar. No entanto, se o seu material demorar mais de um segundo por qualquer motivo, ele não continuará gerando mais subprocessos, sem nunca ter fim.
Portanto, suas coisas nunca são executadas em paralelo, e não em segundo plano; portanto, as variáveis também funcionam como esperado.
Observe que, se você iniciar tarefas adicionais em segundo plano, precisará alterar as
wait
instruções para aguardar apenas osleep
processo especificamente.Se você precisar que seja ainda mais preciso, provavelmente precisará sincronizá-lo com o relógio do sistema e dormir ms em vez de segundos completos.
Como sincronizar com o relógio do sistema? Nenhuma ideia realmente, tentativa estúpida:
Padrão:
Saída: 003511461 010510925 016081282 021643477 028504349 03 ... (continua crescendo)
Sincronizado:
Saída: 002648691 001098397 002514348 001293023 001679137 00 ... (permanece o mesmo)
fonte
sleep
lidar com segundos fracionários?sleep 0.9 || sleep 1
parâmetro inválido é praticamente o único motivo para o sono falhar.sleep 0.9
ser interpretado comosleep 0
por implementações ingênuas (dado que é issoatoi
que faria). Não tenho certeza se isso realmente resultaria em um erro.gdate
no MacOS para fazerdate +%N
o trabalho.)Se você pode reestruturar seu loop em um script / oneliner, a maneira mais simples de fazer isso é com
watch
e suaprecise
opção.Você pode ver o efeito com
watch -n 1 sleep 0.5
- ele mostrará a contagem de segundos, mas ocasionalmente pulará um segundo. Executá-lo comowatch -n 1 -p sleep 0.5
será exibido duas vezes por segundo, a cada segundo, e você não verá pulos.fonte
A execução das operações em um subshell que é executado como um trabalho em segundo plano os faria não interferir tanto com o
sleep
.O único tempo "roubado" a partir de um segundo seria o tempo necessário para iniciar o subshell, de modo que ele eventualmente pularia um segundo, mas esperançosamente com menos frequência do que o código original.
Se o código no subshell acabar usando mais de um segundo, o loop começará a acumular trabalhos em segundo plano e, eventualmente, ficar sem recursos.
fonte
Outra alternativa (se você não pode usar, por exemplo,
watch -p
como Maelstrom sugere) ésleepenh
[página de manual ], projetada para isso.Exemplo:
Observe o
sleep 0.2
simulador fazendo alguma tarefa demorada, comendo cerca de 200ms. Apesar disso, a saída em nanossegundos permanece estável (bem, pelos padrões do SO não em tempo real) - isso acontece uma vez por segundo:Isso está abaixo de 1ms diferente, e nenhuma tendência. Isso é muito bom; você deve esperar pulos de pelo menos 10ms se houver alguma carga no sistema - mas ainda assim não houver desvios ao longo do tempo. Ou seja, você não vai perder um segundo.
fonte
Com
zsh
:Se o seu sono não suportar segundos de ponto flutuante, você pode usar
zsh
'szselect
(depois de azmodload zsh/zselect
):Esses não devem ser desviados enquanto os comandos no loop levarem menos de um segundo para serem executados.
fonte
Eu tinha exatamente o mesmo requisito para um script de shell POSIX, onde todos os ajudantes (usleep, GNUsleep, sleepenh, ...) não estão disponíveis.
consulte: https://stackoverflow.com/a/54494216
fonte