Executar comando a cada X segundos

10

Desejo executar um comando a cada 10 segundos e executá-lo em segundo plano (eliminando assim watch?). Todas as respostas mostram algo como o seguinte, mas isso será executado a cada 11 a 14 segundos. Como pode ser isto alcançado?

while true; do
    # perform command that takes between 1 and 4 seconds
    sleep 10
done
user1032531
fonte
talvez com sinais em tempo real. e manuseio adequado dos segundos bissextos. boa sorte.
mikeserv
@ mikeserv Então, não se incomode em tentar? Não me importo com precisão de mili-segundo, basta dizer meio segundo.
user1032531
O que você está tentando realizar? Acho que uma tarefa que precisa ser executada com esse tipo de frequência geralmente é uma solução alternativa para outro problema.
Sobrique
@Sobrique Exatamente. Estou fazendo uma prova de conceito de um registrador de dados de pesquisa. Na realidade, será programado em outro idioma, mas isso funciona por enquanto.
user1032531

Respostas:

16

E se:

( # In a subshell, for isolation, protecting $!
  while true; do
    perform-command & # in the background
    sleep 10 ;
    ### If you want to wait for a perform-command
    ### that happens to run for more than ten seconds,
    ### uncomment the following line:
    # wait $! ;
    ### If you prefer to kill a perform-command
    ### that happens to run for more than ten seconds,
    ### uncomment the following line instead:
    # kill $! ;
    ### (If you prefer to ignore it, uncomment neither.)
  done
)

ETA: Com todos esses comentários, alternativas e o subshell para proteção extra, isso parece muito mais complicado do que começou. Então, para comparação, veja como era antes de eu começar a me preocupar com , waitou killcom a $!necessidade deles de isolamento:

while true; do perform-command & sleep 10 ; done

O resto é realmente apenas para quando você precisar.

O Sidhekin
fonte
Você pode explicar o raciocínio por trás do seu despejo de código?
Ismael Miguel
2
Além disso, para citar que desembolsar suas obras de código para (como resposta de mikeserv) para que as pessoas não se confundido quando um diferentes se comporta de forma diferente de shell :)
cinza
@IsmaelMiguel Imaginei que os comentários explicariam o raciocínio. Não tenho certeza do que não está claro.
The Sidhekin
1
O manual do bash diz $!: "Expande para a identificação do processo da tarefa colocada mais recentemente em segundo plano" e sleepnão é colocada em segundo plano; portanto, não. :)
The Sidhekin
1
Uau. errado em ambos os aspectos. quando isso aconteceu?
mikeserv
9

Você pode fazer algo como o seguinte em bash, zshou ksh:

SECONDS=0
while   command
do      sleep "$((10-(SECONDS%10)))"
done

Aqui está o que o bashmanual diz sobre $SECONDS:

$SECONDS

  • Cada vez que esse parâmetro é referenciado, o número de segundos desde a chamada do shell é retornado. Se um valor for atribuído a $SECONDS, o valor retornado nas referências subseqüentes será o número de segundos desde a atribuição mais o valor atribuído. Se $SECONDSfor unset, ele perde suas propriedades especiais, mesmo que seja redefinido posteriormente.

Aqui está um exemplo de trabalho:

(   SECONDS=0
    while   sleep   "$((RANDOM%10))"
    do      sleep   "$((10-(SECONDS%10)))"
            echo    "$SECONDS"
    done
)

10
20
30
40
50
60
70
80
90
100
mikeserv
fonte
2
Para evitar a redefinição SECONDSdo sleep "$((10-(($SECONDS-$start_time)%10)))". Você terá que fazer start_time=$SECONDSantes do loop.
Ctrl-alt-delor 10/10/2015
@richard - por que evitá-lo?
mikeserv
porque um dia você o terá em um script que você chama dentro desse loop. E você passará o dia todo se perguntando por que não está funcionando corretamente. Em resumo, ele não aninha, se você redefinir SECONDS. Desovar um sub-shell como em sua edição mais recente também deve evitar esse problema.
Ctrl-alt-delor 10/10/2015
1
@richard - já que a pergunta era sobre um while trueloop, eu não imaginava que nenhum estado de shell pudesse sobreviver a ele. Em qualquer caso, agrupar um loop como esse em seu próprio contexto geralmente é uma prática recomendada. Em uma situação como essa, eu estaria mais preocupado em fazê-lo por trapcausa do que por $SECONDScausa, no entanto.
mikeserv
Obrigado Mike, eu estava pensando em fazer algo semelhante (mas ainda não sabia como). Mas a solução de Sidhekin parece fazer a mesma coisa, não? Não estou qualificado para julgar a melhor resposta, mas me senti obrigado a escolher alguma coisa.
user1032531
3

Somente BASH - Você também pode calcular o tempo gasto pelo seu comando e subtrair de 10:

TIMEFORMAT=$'%0R'
while true; do
    T=$({ time command; } 2>&1)
    sleep $(( 10-T ))

Do BASH man:

TIMEFORMAT O valor desse parâmetro é usado como uma sequência de formato que especifica como as informações de tempo dos pipelines prefixados com a palavra de hora reservada devem ser exibidas. O caractere% introduz uma sequência de escape que é expandida para um valor de tempo ou outras informações. As seqüências de escape e seus significados são os seguintes; as chaves indicam partes opcionais.
%% Um% literal.
% [p] [l] R O tempo decorrido em segundos.
% [p] [l] U O número de segundos de CPU gastos no modo de usuário.
% [p] [l] S O número de segundos de CPU gastos no modo de sistema.
% P A porcentagem de CPU, calculada como (% U +% S) /% R.

Op opcional é um dígito que especifica a precisão, o número de dígitos fracionários após um ponto decimal. Um valor 0 faz com que nenhum ponto decimal ou fração seja gerado.

Dani_l
fonte