Come posso iniziare un cronjob 1 ora dopo ogni giorno?


16

Devo iniziare un cronjob ogni giorno, ma un'ora dopo ogni giorno. Quello che ho finora funziona per la maggior parte, tranne per 1 giorno dell'anno:

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

Quando il giorno dell'anno è 365 il lavoro inizierà alle 5:00, ma il giorno successivo (senza contare un anno bisestile) avrà un giorno dell'anno come 1, quindi il lavoro inizierà alle 1:00. Come posso liberarmi di questa custodia ad angolo?


1
Qualche motivo per non avviarlo ogni 25 ore?
HalosGhost

7
E come lo faresti esattamente? * / 25 nella posizione dell'ora non lo risolverà.
betulla,

@HalosGhost Grazie per il tuo suggerimento! Ho scritto una semplice implementazione basata su.
Giulio Muscarello,

Risposte:


23

La mia soluzione preferita sarebbe quella di avviare il lavoro ogni ora ma fare in modo che lo script stesso controlli se è il momento di eseguire o meno e uscire senza fare nulla 24 volte su 25.

crontab:

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

nella parte superiore di myprog:

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

Se non si desidera apportare modifiche allo script stesso, è anche possibile inserire il segno di spunta "time to run" nella voce crontab ma crea una linea sgradevole lunga:

0 * * * *    [ 0 -eq $(( $(date +\%s) / 3600 \% 25 )) ] && /usr/local/bin/myprog

1
'%' deve essere evitato con una barra rovesciata nel crontab.
betulla

@birch non l'ho mai saputo! Immagino di non aver mai provato prima a includere un% in un crontab. Grazie per la correzione, ho modificato la risposta.
Celada,

1
Questo mi sta bene. Non ho idea di cosa l'OP voglia fare in relazione all'ora legale (primavera in anticipo, in ritardo), ma l'OP dovrebbe adeguarsi di conseguenza.
emory

Sarei leggermente preoccupato per l'arrotondamento nella divisione. Se per qualche motivo il tempo che dateè tornato dal kernel fosse 1msprecedente al tempo previsto per l'esecuzione dello script, il controllo darebbe un risultato errato.
Kasperd,

1
@kasperd Non lo so, suppongo che potresti avere ragione su CPU diverse che riportano tempi diversi. Per quanto riguarda ntpd, fa davvero fatica a spostare l'orologio, non a saltare, in particolare per evitare questo tipo di problema, ma hai ragione, a volte (o ntpdate) a volte può saltare indietro il tempo. Per quanto riguarda il croncalcolo errato del suo ritardo di sospensione, sono abbastanza sicuro che sarebbe considerato un bug! Comunque, punto preso, e una soluzione alternativa sarebbe quella di pianificare il lavoro 30 minuti dopo l'ora, che è meno probabile che causi il problema ... O aggiungere ± 1800 nell'espressione aritmetica prima di prendere il mod Reamainder 3600.
Celada

7

Se il tuo sistema ha systemd, puoi utilizzare eventi timer per questo. È sufficiente definire un nuovo servizio , che dovrebbe contenere il comando / attività che si desidera eseguire, quindi creare un evento timer con l' OnUnitActiveSecopzione:

[Unit]
Description=daily + 1 hour task

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

[Install]
WantedBy=timers.target

Usa lo stesso nome per i file, tranne quello invece di .servicete .timer.

sintetizzando:

  1. Crea un file chiamato job.servicenella /etc/systemd/system/directory.
  2. Riempilo con le informazioni necessarie. È possibile verificare la configurazione utilizzando systemctl status job.service.
  3. Creare un file chiamato job.timerin /etc/systemd/system/.
  4. Riempilo con le informazioni richieste:

    [Unit]
    Description=daily + 1 hour task
    
    [Timer]
    OnUnitActiveSec=25h # run 25 hours after service was last started
    AccuracySec=10min
    
    [Install]
    WantedBy=timers.target
    
  5. Verificare il timer utilizzando systemctl list-timers
  6. Fatto.

Se dovessi allontanarmi dalla semplicità di cron, userei launchd, che richiederebbe meno lavoro del tuo esempio per systemd.
betulla

6

Se non ti dispiace usare qualcosa di diverso da cronjobs, suggerirei l'utilità meno conosciuta at. Basta scrivere uno script wrapper che pianifichi l'esecuzione in 25 ore, quindi chiama il programma. Questa sembra essere la soluzione più pulita.
Ad esempio, potresti scrivere questo in ~ / script.sh:

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

E poi corri semplicemente bash ~/script.shuna volta.

Grazie a @HalosGhost per l'idea di programmare il lavoro una volta ogni 25 ore.


2
Esistono 2 problemi con l'utilizzo ata questo scopo: (1) se il lavoro non viene eseguito correttamente anche una volta, probabilmente non riesce a riprogrammare se stesso e quindi non solo l'esecuzione successiva, ma tutte le esecuzioni future vengono effettivamente annullate fino a quando un umano non se ne accorge, e (2) poiché l'esecuzione del lavoro richiede un tempo diverso da zero, utilizzando l'ingenuo now + 25 hourssignifica che verrà eseguito qualche secondo (o più) più tardi ogni volta, e se questo ritardo si accumulerà nel tempo, rendendolo eventualmente eseguito completamente nel modo sbagliato tempo.
Celada,

2
Hai ragione su # 1; Non sono così sicuro di # 2. Sebbene non disponga di dati, non ritengo che il ritardo tra il cambio dell'orologio e l'attivazione del lavoro in corso e successivamente riprogrammato sia abbastanza grande da essere evidente, soprattutto considerando che la risoluzione di atè limitata a minuti e avvia tutti i lavori a [tempo]: 00.
Giulio Muscarello,

1
@Celada: la pianificazione del atlavoro successivo come prima cosa nello script at evita questi problemi. Tuttavia, se si verifica un errore, l'intera catena viene interrotta, il che può o non può essere desiderato: se il caso d'uso è "esegui questo solo quando funziona", non riavviare è un'ottima funzionalità. Ma se il caso d'uso è "sempre eseguito, anche se l'ultimo fallito" atnon è lo strumento giusto.
vescovo,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.