Lavoro CRON da eseguire l'ultimo giorno del mese


95

Devo creare un lavoro CRON che verrà eseguito l'ultimo giorno di ogni mese. Lo creerò usando cPanel.

Qualsiasi aiuto è apprezzato. Grazie

Risposte:


180

Forse il modo più semplice è fare semplicemente tre lavori separati:

55 23 30 4,6,9,11        * myjob.sh
55 23 31 1,3,5,7,8,10,12 * myjob.sh
55 23 28 2               * myjob.sh

Tuttavia, funzionerà il 28 febbraio, anche negli anni bisestili, quindi, se questo è un problema, dovrai trovare un altro modo.


Tuttavia, di solito è sostanzialmente più facile e corretto eseguire il lavoro il prima possibile il primo giorno di ogni mese, con qualcosa del tipo:

0 0 1 * * myjob.sh

e modificare lo script per elaborare i dati del mese precedente .

Ciò elimina qualsiasi problema che potresti incontrare nel capire quale giorno è l'ultimo del mese e garantisce anche che tutti i dati per quel mese siano disponibili, supponendo che tu stia elaborando i dati. L'esecuzione da cinque minuti a mezzanotte dell'ultimo giorno del mese potrebbe farti perdere tutto ciò che accade tra allora e la mezzanotte.

Questo è il solito modo per farlo comunque, per la maggior parte dei lavori di fine mese.


Se ancora davvero desidera eseguire l'ultimo giorno del mese, una possibilità è quella di rilevare semplicemente se domani è il primo (sia come parte dello script, oppure nel crontab stesso).

Quindi, qualcosa come:

55 23 28-31 * * [[ "$(date --date=tomorrow +\%d)" == "01" ]] && myjob.sh

dovrebbe essere un buon inizio, ammesso che tu abbia un dateprogramma relativamente intelligente .

Se il tuo dateprogramma non è abbastanza avanzato da darti date relative, puoi semplicemente mettere insieme un programma molto semplice per darti il ​​giorno del mese di domani (non hai bisogno di tutta la potenza di date), come ad esempio:

#include <stdio.h>
#include <time.h>

int main (void) {
    // Get today, somewhere around midday (no DST issues).

    time_t noonish = time (0);
    struct tm *localtm = localtime (&noonish);
    localtm->tm_hour = 12;

    // Add one day (86,400 seconds).

    noonish = mktime (localtm) + 86400;
    localtm = localtime (&noonish);

    // Output just day of month.

    printf ("%d\n", localtm->tm_mday);

    return 0;
}

e poi usa (supponendo che tu l'abbia chiamato tomdom"giorno del mese di domani"):

55 23 28-31 * * [[ "$(tomdom)" == "1" ]] && myjob.sh

Anche se potresti prendere in considerazione l'aggiunta del controllo degli errori poiché entrambi time()e mktime()possono tornare -1se qualcosa va storto. Il codice precedente, per motivi di semplicità, non ne tiene conto.


7
sì, può essere più facile correre ogni primo giorno invece che l'ultimo giorno :)
Utku Dalmaz

1
Il primo giorno del mese in effetti. Ecco come apparirà il codice in PHP $ date = new DateTime ('2013-03-01'); $ date-> modify ('- 1 month'); $ previousMonth = $ date-> format ('Y-m'); // $ previousMonth è ora 2013-02. Crea query per recuperare i prodotti per il mese precedente.
Lamy

I dati dell'anno bisestile, 29 febbraio, andranno persi, dobbiamo considerare anche questo. La risposta di Thunder Rabbit di seguito lo considera, ma cron viene eseguito due volte a febbraio dell'anno bisestile
Hari Swaminathan,

1
@Hari, la soluzione preferita sarebbe quella di eseguire il primo del mese e raccogliere i dati del mese precedente. Il 29 febbraio non sarebbe mancato in quel caso.
paxdiablo

1
Considerando gli anni bisestili: e se non ti dispiace correre due volte: 55 23 28,29 2 * myjob.sh
radiantRazor

52

C'è un metodo leggermente più breve che può essere utilizzato simile a uno di quelli sopra. Questo è:

[ $(date -d +1day +%d) -eq 1 ] && echo "last day of month"

Inoltre, la voce crontab potrebbe essere aggiornata per controllare solo dal 28 al 31 poiché è inutile eseguirla negli altri giorni del mese. Che ti darebbe:

0 23 28-31 * * [ $(date -d +1day +%d) -eq 1 ] && myscript.sh

Non sono riuscito a farlo funzionare come una voce crontab (qualcosa deve essere evitato credo). Tuttavia ha funzionato bene in uno script di shell chiamato da crontab. Cordiali saluti, l'errore che ho ricevuto è stato /bin/sh: -c: line 1: unexpected EOF while looking for matching ')'.
Mark Rajcok

13
Funziona alla grande. Nel file crontab è necessario eseguire l'escape%. Quindi[ $(date -d +1day +\%d) -eq 1 ] && run_job
ColinM

Davvero un bel trucco! Ma la domanda è stata contrassegnata posixe la data POSIX non supporta "-d +1day": - \ Una soluzione più complicata (e brutta) sarebbe:[ `date +\%d` -eq `cal | xargs echo | awk '{print $NF}'` ] && myscript.sh
ckujau

18

Imposta un cron job da eseguire il primo giorno del mese. Quindi cambia l'orologio del sistema in modo che sia un giorno avanti.


10
il che significa che l'orologio di sistema sarà sempre sbagliato. Scusa ma penso che questo causerà più problemi. -1 allora.
Rudy

69
Adesso so come si sentiva Galileo.
Tom Anderson

7
@iconoclast: mi piacerebbe considerarlo più un koan che uno scherzo.
Tom Anderson,

14
Penso che questa sia un'idea davvero brutta E geniale. Non fatelo a casa, ragazzi.
Pascal

11
@pascalbetz Oh, le persone possono farlo a casa, ma in realtà non dovrebbero farlo al lavoro.
Tom Anderson

14

Che ne dici di questo, dopo Wikipedia?

55 23 L * * /full/path/to/command

Bene, che ne dici? Quello: "Errori del giorno del mese non validi nel file crontab, impossibile installare. Riprovare la stessa modifica?"
webjunkie

12
Giusto per essere chiari, quella voce di wikipedia menziona anche che "L" non è standard.
sdupton

10

Adattando la soluzione di paxdiablo, corro il 28 e 29 febbraio. I dati del 29 sovrascrivono il 28.

# min  hr  date     month          dow
  55   23  31     1,3,5,7,8,10,12   * /path/monthly_copy_data.sh
  55   23  30     4,6,9,11          * /path/monthly_copy_data.sh
  55   23  28,29  2                 * /path/monthly_copy_data.sh

4
A meno che, ovviamente, il lavoro abbia qualche aspetto distruttivo come cancellare tutti i dati mentre vengono elaborati :-)
paxdiablo

Questa è in realtà un'opzione indolore (meno tecnica) e potresti non preoccuparti che tre volte in 4 anni otterrai il cronjob troppo presto se ometti semplicemente il file ,29.
Matt

@ Matt: Umm, non vuoi dire che verrà eseguito un giorno troppo presto una volta ogni quattro anni se la tua voce crontab dice 55   23   28    2?
G-Man dice "Reinstate Monica" l'

@ G-Man Sì, hai ragione, e per di più, lo fai girare una seconda volta il 29.
Matt

8

È possibile impostare un cron job da eseguire ogni giorno del mese e farlo eseguire uno script di shell come il seguente. Questo script determina se il numero del giorno di domani è inferiore a quello di oggi (cioè se domani è un nuovo mese) e poi fa quello che vuoi.

TODAY=`date +%d`
TOMORROW=`date +%d -d "1 day"`

# See if tomorrow's day is less than today's
if [ $TOMORROW -lt $TODAY ]; then
echo "This is the last day of the month"
# Do stuff...
fi

Non sono sicuro di cosa guadagni controllando se è inferiore a oggi - dovresti solo essere in grado di controllare se è il primo. A meno che il primo giorno del mese non sia il 2 o il 3 :-)
paxdiablo

7

Per un metodo più sicuro in un crontab basato sulla soluzione @Indie (utilizzare il percorso assoluto per date+ $()non funziona su tutti i sistemi crontab):

0 23 28-31 * * [ `/bin/date -d +1day +\%d` -eq 1 ] && myscript.sh


5
#########################################################
# Memory Aid 
# environment    HOME=$HOME SHELL=$SHELL LOGNAME=$LOGNAME PATH=$PATH
#########################################################
#
# string         meaning
# ------         -------
# @reboot        Run once, at startup.
# @yearly        Run once a year, "0 0 1 1 *".
# @annually      (same as @yearly)
# @monthly       Run once a month, "0 0 1 * *".
# @weekly        Run once a week, "0 0 * * 0".
# @daily         Run once a day, "0 0 * * *".
# @midnight      (same as @daily)
# @hourly        Run once an hour, "0 * * * *".
#mm     hh      Mday    Mon     Dow     CMD # minute, hour, month-day month DayofW CMD
#........................................Minute of the hour
#|      .................................Hour in the day (0..23)
#|      |       .........................Day of month, 1..31 (mon,tue,wed)
#|      |       |       .................Month (1.12) Jan, Feb.. Dec
#|      |       |       |        ........day of the week 0-6  7==0
#|      |       |       |        |      |command to be executed
#V      V       V       V        V      V
*       *       28-31   *       *       [ `date -d +'1 day' +\%d` -eq 1 ] && echo "Tomorrow is the first today now is  `date`" >> ~/message
1       0       1       *       *       rm -f ~/message
*       *       28-31   *       *       [ `date -d +'1 day' +\%d` -eq 1 ] && echo "HOME=$HOME LOGNAME=$LOGNAME SHELL = $SHELL PATH=$PATH" 

5

Per l'implementazione di cron di AWS Cloudwatch (Scheduling Lambdas, ecc.) Funziona:

55 23 L * ? *

In esecuzione alle 23:55 dell'ultimo giorno di ogni mese.



3

Puoi semplicemente connettere tutte le risposte in una riga cron e usarle solo date comando.

Basta controllare la differenza tra il giorno del mese che è oggi e sarà domani:

0 23 * * * root [ $(expr $(date +\%d -d '1 days') - $(date +\%d)  ) -le 0 ]  && echo true

Se questa differenza è inferiore a 0 significa che cambiamo il mese e c'è l'ultimo giorno del mese.


3
55 23 28-31 * * echo "[ $(date -d +1day +%d) -eq 1 ] && my.sh" | /bin/bash 

1
echeggiare il comando e inviarlo a bash è il modo migliore quando si lavora con cron. Dato che cron usa sh e non bash. Controlla dove il tuo bash sta usando 'which bash'. Su FreeBSD è / usr / local / bin / bash, su Linux / bin / bash.
Paperino

2

Che dire di questo?

modifica l' .bashprofileaggiunta dell'utente :

export LAST_DAY_OF_MONTH=$(cal | awk '!/^$/{ print $NF }' | tail -1)

Quindi aggiungi questa voce a crontab:

mm hh * * 1-7 [[ $(date +'%d') -eq $LAST_DAY_OF_MONTH ]] && /absolutepath/myscript.sh

0

L'ultimo giorno del mese può essere 28-31 a seconda del mese in cui si trova (febbraio, marzo ecc.). Tuttavia, in entrambi i casi, il giorno successivo è sempre il primo del mese successivo. Quindi possiamo usarlo per assicurarci di eseguire un lavoro sempre l'ultimo giorno di un mese utilizzando il codice seguente:

0 8 28-31 * * [ "$(date +%d -d tomorrow)" = "01" ] && /your/script.sh
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.