Pianifica l'ultimo giorno di ogni mese


10

Ho letto da un'istruzione per programmare una sceneggiatura l'ultimo giorno del mese:

Nota:
il lettore astuto potrebbe chiedersi come sarebbe possibile impostare un comando da eseguire l'ultimo giorno di ogni mese perché non è possibile impostare il valore di dayofmonth per coprire ogni mese. Questo problema ha afflitto i programmatori Linux e Unix e ha generato diverse soluzioni. Un metodo comune è aggiungere un'istruzione if-then che utilizza il comando date per verificare se la data di domani è 01:

00 12 * * * if [`date +%d -d tomorrow` = 01 ] ; then ; command1

Questo controlla ogni giorno alle 12:00 per vedere se è l'ultimo giorno del mese e, in tal caso, cron esegue il comando.

inserisci qui la descrizione dell'immagine

Come [`date +%d -d tomorrow` = 01 ]funziona?
È corretto affermare then; command1?


Sei sicuro che sia letteralmente quello che dice? Come scritto qui in realtà non funziona.
Michael Homer,

Ho pubblicato l'istantanea. @ MichaelHomer
Calculus

Grazie! Ho smanettato con la formattazione per farlo corrispondere - non è ancora del tutto corretto, ma è quello che dice l'immagine.
Michael Homer,

1
Mancante ; endif?
danblack,

Non funziona Contiene errori di sintassi: nessuno spazio dopo [e nessuno fialla fine. Inoltre, %è speciale nei crontab.
Kusalananda

Risposte:


17

Astratto

Il codice corretto dovrebbe essere:

#!/bin/sh
[ "$#" -eq 0 ] && echo "Usage: $0 command [args]" && exit 1
[ "$(date -d tomorrow +'%d')" = 01 ] || exit 0
exec "$@"

Chiama questo script end_of_month.she la chiamata in cron è semplicemente:

00 12 28-31 * * /path/to/script/end_of_month.sh command

Ciò eseguirà lo script end_of_month(che verificherà internamente che il giorno è l'ultimo giorno del mese) solo nei giorni 28, 29, 30 e 31. Non è necessario controllare la fine del mese in qualsiasi altro giorno.

Vecchio post.

Questa è una citazione dal libro "Linux Command Line and Shell Scripting Bible" di Richard Blum, Christine Bresnahan pp 442, Third Edition, John Wiley & Sons © 2015.

Sì, è quello che dice, ma è sbagliato / incompleto:

  • Manca una chiusura fi.
  • Ha bisogno di spazio tra [e il seguente `.
  • Si consiglia vivamente di utilizzare $ (...) invece di `…`.
  • È importante usare virgolette intorno ad espansioni come"$(…)"
  • C'è un ulteriore ;dopothen

Come lo so? (bene, per esperienza ☺) ma puoi provare Shellcheck . Incolla il codice dal libro (dopo gli asterischi) e ti mostrerà gli errori sopra elencati più uno "shebang mancante". Uno script senza errori in Shellcheck è questo:

#!/bin/sh if [ "$(date +%d -d tomorrow)" = 01 ] ; then script.sh; fi

Quel sito funziona perché quello che è stato scritto è "codice shell". Questa è una sintassi che funziona in molte shell.

Alcuni problemi che la shellcheck non menziona sono:

  • Si presuppone che il comando date sia la versione della data GNU. Quello con -dun'opzione che accetta tomorrowcome valore (busybox ha un'opzione -d ma non capisce domani e BSD ha -dun'opzione ma non è correlato alla "visualizzazione" del tempo).

  • È meglio impostare il formato dopo tutte le opzioni date -d tomorrow +'%d'.

  • L'ora di inizio cron è sempre in ora locale, il che può far iniziare un lavoro un'ora prima o dopo un giorno esatto se il DST (ora legale) è stato impostato o non impostato.

Quello che abbiamo fatto è uno script di shell che potrebbe essere chiamato con cron. Possiamo modificare ulteriormente lo script per accettare argomenti del programma o comando per eseguire, in questo modo (infine, il codice corretto):

#!/bin/sh
[ "$#" -eq 0 ] && echo "Usage: $0 command [args]" && exit 1
[ "$(date -d tomorrow +'%d')" = 01 ] || exit 0
exec "$@"

Chiama questo script end_of_month.she la chiamata in cron è semplicemente:

00 12 28-31 * * /path/to/script/end_of_month.sh command

Ciò eseguirà lo script end_of_month(che verificherà internamente che il giorno è l'ultimo giorno del mese) solo nei giorni 28, 29, 30 e 31. Non è necessario controllare la fine del mese in qualsiasi altro giorno.

Assicurarsi che sia incluso il percorso corretto. Il PERCORSO all'interno di cron non sarà (probabilmente) uguale al PERCORSO dell'utente.

Si noti che esiste uno script di fine mese testato (come indicato di seguito) che potrebbe chiamare molte altre utilità o script.

Questo eviterà anche il problema aggiuntivo che cron genera con l'intera riga di comando:

  • Cron divide la riga di comando su qualsiasi %anche se citato con 'o "(funziona solo \qui). Questo è un modo comune in cui i processi cron falliscono.

Puoi verificare se lo end_of_month.shscript funziona correttamente in qualche data (senza attendere la fine del mese per scoprire che non funziona) testandolo con faketime:

$ faketime 2018/10/31 ./end_of_month echo "Command will be executed...."
Command will be executed....

ast-open date(o l' dateintegrato di ksh93 se ksh93 è stato creato come parte di ast-open) supporta date -d tomorrow +%so date +%s tomorrow).
Stéphane Chazelas,

2
L'esperienza ha dimostrato (molte volte) che è molto meglio testare correttamente lo script con faketime piuttosto che aspettare fino alla fine del mese per scoprire che il lavoro pianificato non ha funzionato. Come scoprire che * * * * * echo "$(date -u +'date %c')" >>~/testfilenon funzionerà perché uno ha dimenticato di citare il \%(che diventa difficile eseguire il debug se è possibile solo un tentativo ogni mese). @Kusalananda
Isaac,

8

Supponendo che gli errori di sintassi siano corretti e che il comando sia stato leggermente riformulato per essere meno dettagliato:

00 12 28-31 * * [ "$( date -d tomorrow +\%d )" != "01" ] || command1

Questo funziona date +%d -d tomorrow(supponendo che sia GNU dateche viene utilizzato) per ottenere la data di domani come un numero a due cifre. Se il numero non lo è 01, oggi non è l'ultimo giorno del mese. In tal caso, i test riesce e command1viene non eseguito. Il lavoro viene eseguito a mezzogiorno nei giorni che potrebbero essere l'ultimo giorno del mese.

Il comando originale:

00 12 * * * if [`date +%d -d tomorrow` = 01 ] ; then ; command1

Questo ha alcuni problemi:

  • Nessuno spazio dopo [.
  • A ;subito dopo then.
  • %è speciale nelle specifiche del lavoro cron e deve essere sottoposto a escape come \%(vedi man 5 crontab).
  • Non c'è un finale fialla fine che corrisponda al if.

2
Il problema con l'utilizzo [ ... ] && command1invece di if...è che in giorni che non sono l'ultimo giorno del mese, il processo cron terminerà con uno stato di uscita diverso da zero e potrebbe essere necessario segnalare l' errore . L'utilizzo [ "$(...)" != 01 ] || command1è un altro modo per evitare il problema.
Stéphane Chazelas,

1
Un altro problema con il codice originale è ;tra thene command1.
Stéphane Chazelas,

@ StéphaneChazelas Un altro motivo per cui non mi piacciono i one-liner, sono difficili da leggere.
Kusalananda

La differenza oraria tra esecuzioni consecutive del comando potrebbe non essere un numero intero di (24 ore) giorni poiché una modifica dell'ora legale sposta l'ora di inizio di cron.
Isaac,

3
@cat Non importa come si cita, o "o '(tranne \), il segno di percentuale %farà in modo che cron interrompa la linea in due parti. Questo è un modo normale per far fallire cron.
Isaac,

-1

Per pianificare l'ultimo giorno di ogni mese, si può provare: 0 0 15,L * *.

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.