Como posso iniciar um cronjob 1 hora depois por dia?

16

Preciso iniciar um cronjob todos os dias, mas uma hora depois a cada dia. O que tenho até agora funciona em grande parte, exceto em 1 dia do ano:

0 0 * * * sleep $((3600 * (10#$(date +\%j) \% 24))) && /usr/local/bin/myprog

Quando o dia do ano for 365, o trabalho começará às 5:00, mas o dia seguinte (sem contar um ano bissexto) terá o dia do ano como 1, portanto, o trabalho começará às 13:00. Como posso me livrar dessa caixa de canto?

bétula
fonte
11
Algum motivo para não iniciá-lo a cada 25 horas?
precisa saber é o seguinte
7
E como exatamente você faria isso? * / 25 na posição da hora não o resolverá.
bétula
@HalosGhost Obrigado pela sua sugestão! Eu escrevi uma implementação simples com base em at.
Giulio Muscarello

Respostas:

23

Minha solução preferida seria iniciar o trabalho a cada hora, mas fazer com que o próprio script verifique se é hora de executar ou não e sair sem fazer nada 24 vezes em 25.

crontab:

0 * * * *    /usr/local/bin/myprog

no topo de myprog:

[ 0 -eq $(( $(date +%s) / 3600 % 25 )) ] || exit 0

Se você não quiser fazer alterações no próprio script, também pode colocar a verificação "tempo para executar" na entrada crontab, mas isso cria uma longa linha desagradável:

0 * * * *    [ 0 -eq $(( $(date +\%s) / 3600 \% 25 )) ] && /usr/local/bin/myprog
Celada
fonte
11
'%' precisa ser escapado com uma barra invertida no crontab.
bétula
@ Birch Eu nunca soube disso! Acho que nunca antes tentei incluir um% em um crontab. Obrigado pela correção, eu editei a resposta.
Celada
11
Isso parece bom para mim. Não tenho idéia do que o OP quer fazer com relação ao horário de verão (primavera à frente, atraso), mas o OP deve fazer os ajustes necessários.
Emory
Eu ficaria um pouco preocupado com o arredondamento na divisão. Se por alguma razão o tempo que datevoltou a partir do kernel foi 1msantes do tempo que você espera que o script para ser executado, a verificação daria um resultado incorreto.
precisa saber é o seguinte
11
@ kasperd Eu não sei, suponho que você esteja certo sobre diferentes CPUs relatando horários diferentes. Quanto a ntpd, é difícil apenas girar o relógio, não pular, especificamente para evitar esse tipo de problema, mas você está certo, ntpdateàs vezes ele pode atrasar o tempo. Quanto ao croncálculo incorreto do atraso do sono, tenho certeza de que isso seria considerado um bug! Mesmo assim, é preciso agendar o trabalho 30 minutos após a hora com menor probabilidade de causar o problema ... Ou acrescentar ± 1800 na expressão aritmética antes de usar o mod 3600 do restante.
Celada
7

Se o seu sistema possui systemd, você pode usar eventos de temporizadores para isso. Basta definir um novo serviço , que deve conter o comando / tarefa que você deseja executar e, em seguida, criar um evento de timer com a OnUnitActiveSecopção:

[Unit]
Description=daily + 1 hour task

[Timer]
OnUnitActiveSec=25h # run 25 hours after service was last started
AccuracySec=10min

[Install]
WantedBy=timers.target

Use o mesmo nome para os arquivos, exceto que, em vez de .service você usar .timer.

Sintetizando:

  1. Crie um arquivo chamado job.service no /etc/systemd/system/diretório
  2. Preencha com as informações necessárias.Você pode verificar a configuração usando systemctl status job.service.
  3. Crie um arquivo chamado job.timer no /etc/systemd/system/.
  4. Preencha com as informações necessárias:

    [Unit]
    Description=daily + 1 hour task
    
    [Timer]
    OnUnitActiveSec=25h # run 25 hours after service was last started
    AccuracySec=10min
    
    [Install]
    WantedBy=timers.target
    
  5. Verifique o cronômetro usando systemctl list-timers
  6. Feito.
Braiam
fonte
Se eu me afastasse da simplicidade do cron, usaria o launchd, o que exigiria menos trabalho do que o seu exemplo para o systemd.
bétula
6

Se você não se importa em usar algo além de cronjobs, sugiro o utilitário menos conhecido at. Basta escrever um script de wrapper que planeje ser executado em 25 horas e, em seguida, chame seu programa. Esta parece ser a solução mais limpa.
Por exemplo, você pode escrever isso em ~ / script.sh:

echo "bash ~/script.sh" | at now + 25 hours
/usr/bin/yourprogram

E então simplesmente execute bash ~/script.shuma vez.

Agradecemos a @HalosGhost pela idéia de agendar o trabalho uma vez em 25 horas.

Giulio Muscarello
fonte
2
Existem 2 problemas com o uso atpara esse fim: (1) se a tarefa falhar em ser executada corretamente mesmo uma vez, provavelmente também não será reprogramada e, em seguida, não apenas a próxima execução, mas todas as execuções futuras serão efetivamente canceladas até que um ser humano perceba, e (2) uma vez que o trabalho demora algum tempo diferente de zero, usando os ingênuos now + 25 hourssignifica que ele será executado alguns segundos (ou mais) mais tarde a cada vez, e esse atraso aumentará com o tempo, tornando-o eventualmente errado. Tempo.
Celada
2
Você está correto sobre o número 1; Não tenho tanta certeza sobre o # 2. Embora eu não tenha nenhum dado, não acho que o atraso entre a alteração do relógio e o trabalho no momento sendo acionado e subsequentemente reagendado seja grande o suficiente para ser notado - especialmente considerando que a resolução de até limitada a minutos e inicia todos os trabalhos às [hora]: 00.
Giulio Muscarello
11
@Celada: Programando o próximo attrabalho primeira coisa no no roteiro evita esses problemas. Não obstante, se ocorrer um erro, toda a cadeia será interrompida, o que pode ou não ser desejado: se o caso de uso "executar apenas quando estiver funcionando", não reiniciar é um ótimo recurso. Mas se o caso de uso for "sempre executado, mesmo se o último falhar", atnão será a ferramenta certa.
bispo