Estou executando este loop para verificar e imprimir algumas coisas a cada segundo. No entanto, como os cálculos levam talvez algumas centenas de milissegundos, o tempo impresso às vezes salta um segundo.
Existe alguma maneira de escrever um loop que tenha a garantia de obter uma impressão a cada segundo? (Desde que, é claro, os cálculos no loop levem 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
Para ficar um pouco mais próximo do código original, o que eu faço é:
Isso muda um pouco a semântica: se o seu material demorar menos de um segundo, ele simplesmente aguardará o segundo inteiro passar. No entanto, se o seu material demorar mais de um segundo por qualquer motivo, ele não continuará gerando ainda mais subprocessos sem nunca terminar.
Portanto, suas coisas nunca são executadas em paralelo, e não em segundo plano, portanto, as variáveis também funcionam conforme o esperado.
Observe que, se você também iniciar tarefas adicionais em segundo plano, precisará alterar a
wait
instrução para aguardar apenas osleep
processo especificamente.Se você precisar que ele 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? Não faço ideia, tentativa estúpida:
Predefinição:
Saída: 003511461 010510925 016081282 021643477 028504349 03... (continua crescendo)
Sincronizado:
Saída: 002648691 001098397 002514348 001293023 001679137 00... (permanece igual)
Se você puder 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
produzirá duas vezes por segundo, a cada segundo, e você não verá nenhum pulo.Executar as operações em um subshell que é executado como um trabalho em segundo plano faria com que elas não interferissem tanto com o
sleep
.O único tempo "roubado" de um segundo seria o tempo necessário para iniciar o subshell, então ele eventualmente pularia um segundo, mas com menos frequência do que o código original.
Se o código no subshell usar mais de um segundo, o loop começará a acumular trabalhos em segundo plano e, eventualmente, ficar sem recursos.
Outra alternativa (se você não pode usar, por exemplo,
watch -p
como Maelstrom sugere) ésleepenh
[ manpage ], que é projetado para isso.Exemplo:
Observe a
sleep 0.2
simulação fazendo alguma tarefa demorada consumindo cerca de 200ms. Apesar disso, a saída de nanossegundos permanece estável (bem, pelos padrões do sistema operacional não em tempo real) - isso acontece uma vez por segundo:Isso é menos de 1ms diferente e nenhuma tendência. Isso é muito bom; você deve esperar saltos de pelo menos 10 ms se houver alguma carga no sistema - mas ainda sem desvios ao longo do tempo. Ou seja, você não vai perder um segundo.
Com
zsh
:Se o seu sono não suportar segundos de ponto flutuante, você pode usar
zsh
'szselect
(depois de umzmodload zsh/zselect
):Eles não devem se deslocar enquanto os comandos no loop levarem menos de um segundo para serem executados.
Eu tinha exatamente o mesmo requisito para um script de shell POSIX, onde todos os auxiliares (usleep, GNUsleep, sleepenh, ...) não estão disponíveis.
veja: https://stackoverflow.com/a/54494216