CRON-Job, der am letzten Tag des Monats ausgeführt wird

95

Ich muss einen CRON-Job erstellen, der am letzten Tag eines jeden Monats ausgeführt wird. Ich werde es mit cPanel erstellen.

Jede Hilfe wird geschätzt. Vielen Dank

Utku Dalmaz
quelle

Antworten:

180

Möglicherweise ist der einfachste Weg, einfach drei separate Jobs zu erledigen:

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

Das wird jedoch am 28. Februar laufen, auch in Schaltjahren. Wenn dies ein Problem ist, müssen Sie einen anderen Weg finden.


In der Regel ist es jedoch wesentlich einfacher und korrekter, den Job so schnell wie möglich am ersten Tag eines jeden Monats auszuführen. Dabei gilt Folgendes:

0 0 1 * * myjob.sh

und ändern Sie das Skript, um das vorherige zu verarbeiten Monat Daten.

Dies beseitigt alle Probleme, die auftreten können, wenn Sie herausfinden, welcher Tag der letzte des Monats ist, und stellt außerdem sicher, dass alle Daten für diesen Monat verfügbar sind, vorausgesetzt, Sie verarbeiten Daten. Wenn Sie am letzten Tag des Monats zwischen fünf Minuten und Mitternacht laufen, verpassen Sie möglicherweise alles, was zwischen diesem Zeitpunkt und Mitternacht passiert.

Dies ist ohnehin die übliche Vorgehensweise für die meisten Jobs am Monatsende.


Wenn du noch wirklich es am letzten Tag des Monats ausgeführt werden sollen, ist eine Option, einfach zu erkennen , ob morgen die ersten (entweder als Teil des Skripts oder in dem crontab selbst) ist.

Also so etwas wie:

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

sollte ein guter Anfang sein, vorausgesetzt, Sie haben ein relativ intelligentes dateProgramm.

Wenn Ihr dateProgramm nicht weit genug fortgeschritten ist, um Ihnen relative Daten zu geben, können Sie einfach ein sehr einfaches Programm zusammenstellen, um Ihnen den morgigen Tag des Monats zu geben (Sie benötigen nicht die volle Leistung von date), wie zum Beispiel:

#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;
}

und dann verwenden (vorausgesetzt, Sie haben es tomdomfür "morgigen Tag des Monats" genannt):

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

Obwohl Sie da beide prüfen , sollten die Fehlerprüfung Hinzufügen time()und mktime()zurückgeben kann , -1wenn etwas schief geht. Der obige Code berücksichtigt dies aus Gründen der Einfachheit nicht.

paxdiablo
quelle
6
Ja, es kann einfacher sein, an jedem ersten Tag statt am letzten Tag zu laufen :)
Utku Dalmaz
1
1. Tag des Monats in der Tat. So sieht der Code in PHP aus $ date = new DateTime ('2013-03-01'); $ date-> modify ('- 1 Monat'); $ previousMonth = $ date-> format ('Y-m'); // $ previousMonth ist jetzt 2013-02. Erstellen Sie eine Abfrage, um Produkte für den Vormonat abzurufen.
Lamy
Schaltjahr 29. Februar Daten gehen verloren, das müssen wir auch berücksichtigen. Thunder Rabbit Antwort unten berücksichtigen, dass aber Cron zweimal im Februar des Schaltjahres laufen
Hari Swaminathan
1
@ Hari, die bevorzugte Lösung wäre, am ersten des Monats zu laufen und die Daten des Vormonats zu sammeln. Der 29. Februar würde in diesem Fall nicht fehlen.
Paxdiablo
1
In Anbetracht der Schaltjahre: und wenn es Ihnen nichts ausmacht, zweimal zu laufen: 55 23 28,29 2 * myjob.sh
radiantRazor
52

Es gibt eine etwas kürzere Methode, die ähnlich wie die oben beschriebene verwendet werden kann. Das ist:

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

Außerdem könnte der Crontab-Eintrag so aktualisiert werden, dass er nur am 28. bis 31. überprüft wird, da es sinnlos ist, ihn an den anderen Tagen des Monats auszuführen. Welches würde Ihnen geben:

0 23 28-31 * * [ $(date -d +1day +%d) -eq 1 ] && myscript.sh
Indie
quelle
Ich konnte dies nicht als Crontab-Eintrag zum Laufen bringen (ich denke, es muss etwas entkommen). In einem Shell-Skript, das von crontab aufgerufen wurde, funktionierte es jedoch einwandfrei. Zu Ihrer Information, der Fehler, den ich bekam, war /bin/sh: -c: line 1: unexpected EOF while looking for matching ')'.
Mark Rajcok
13
Das funktioniert super. In der Crontab-Datei muss% maskiert werden. Also[ $(date -d +1day +\%d) -eq 1 ] && run_job
ColinM
Ein schöner Trick! Aber die Frage wurde markiert posixund das POSIX-Datum unterstützt nicht "-d +1day": - \ Eine kompliziertere (und hässlichere) Lösung wäre:[ `date +\%d` -eq `cal | xargs echo | awk '{print $NF}'` ] && myscript.sh
ckujau
18

Richten Sie einen Cron-Job ein, der am ersten Tag des Monats ausgeführt wird. Ändern Sie dann die Uhr des Systems so, dass sie einen Tag voraus ist.

Tom Anderson
quelle
10
Dies bedeutet, dass die Systemuhr immer falsch ist. Entschuldigung, aber ich denke, dies wird mehr Probleme verursachen. -1 dann.
Rudy
68
Jetzt weiß ich, wie sich Galileo gefühlt hat.
Tom Anderson
7
@iconoclast: Ich würde es gerne eher als Koan als als Witz betrachten.
Tom Anderson
14
Ich denke, das ist eine wirklich schlechte UND brillante Idee. Mach das nicht zu Hause, Kinder.
Pascal
11
@pascalbetz Oh, die Leute können es zu Hause machen, aber sie sollten es wirklich nicht bei der Arbeit machen.
Tom Anderson
14

Was ist mit diesem nach Wikipedia?

55 23 L * * /full/path/to/command
Piohen
quelle
Was ist damit? Das: "Fehler am Tag des Monats in der Crontab-Datei können nicht installiert werden. Möchten Sie dieselbe Bearbeitung wiederholen?"
Webjunkie
12
Um ganz klar zu sein, erwähnt dieser Wikipedia-Eintrag auch, dass "L" nicht Standard ist.
sdupton
10

Ich passe die Lösung von paxdiablo an und laufe am 28. und 29. Februar. Die Daten vom 29. überschreiben den 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
Donnerkaninchen
quelle
4
Es sei denn natürlich, der Job hat einen destruktiven Aspekt wie das Löschen aller Daten während der Verarbeitung :-)
paxdiablo
Dies ist eigentlich eine schmerzlose Option (am wenigsten technisch), und es ist Ihnen vielleicht egal, dass Sie in 4 Jahren dreimal zu früh den Cronjob bekommen, wenn Sie den einfach weglassen ,29.
Matt
@Matt: Ähm, meinst du nicht, dass es einmal in vier Jahren einen Tag zu früh laufen wird, wenn dein Crontab-Eintrag sagt 55   23   28    2?
G-Man sagt "Reinstate Monica"
@ G-Man Ja, du hast recht, und obendrein hast du es am 29. ein zweites Mal laufen lassen.
Matt
8

Sie können einen Cron-Job einrichten, der an jedem Tag des Monats ausgeführt wird, und ein Shell-Skript wie das folgende ausführen lassen. Dieses Skript ermittelt, ob die Nummer des morgigen Tages kleiner als die des heutigen Tages ist (dh ob morgen ein neuer Monat ist), und macht dann, was Sie wollen.

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
thomson_matt
quelle
Ich bin mir nicht sicher, was Sie gewinnen, wenn Sie prüfen, ob es weniger als heute ist - Sie sollten nur prüfen können, ob es das erste ist. Es sei denn, der erste Tag des Monats kann der 2. oder 3. sein :-)
paxdiablo
7

Für eine sicherere Methode in einer Crontab, die auf der @ Indie-Lösung basiert (verwenden Sie den absoluten Pfad zu date+, $()funktioniert nicht auf allen Crontab-Systemen):

0 23 28-31 * * [ `/bin/date -d +1day +\%d` -eq 1 ] && myscript.sh
Zigarn
quelle
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" 
Leslie Satenstein
quelle
5

Für die AWS Cloudwatch-Cron-Implementierung (Scheduling Lambdas usw.) funktioniert dies:

55 23 L * ? *

Laufen am letzten Tag eines jeden Monats um 23.55 Uhr.

Dan Herman
quelle
4
00 23 * * * [[ $(date +'%d') -eq $(cal | awk '!/^$/{ print $NF }' | tail -1) ]] && job

Schauen Sie sich eine verwandte Frage im unix.com-Forum an.

loswerden
quelle
3

Sie können einfach alle Antworten in einer Cron-Zeile verbinden und nur den dateBefehl verwenden.

Überprüfen Sie einfach den Unterschied zwischen dem Tag des Monats, der heute ist und morgen sein wird:

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

Wenn diese Differenz unter 0 liegt, bedeutet dies, dass wir den Monat ändern und es den letzten Tag des Monats gibt.

Lukasz Stelmach
quelle
3
55 23 28-31 * * echo "[ $(date -d +1day +%d) -eq 1 ] && my.sh" | /bin/bash 
Donald Duck
quelle
1
Wenn Sie mit cron arbeiten, ist es am besten, den Befehl zu wiederholen und ihn in bash zu leiten. Da Cron Sh verwendet und nicht Bash. Überprüfen Sie, wo Ihre Bash 'welche Bash' verwendet. Unter FreeBSD ist es / usr / local / bin / bash, unter Linux / bin / bash.
Donald Duck
2

Was ist damit?

Benutzer .bashprofilehinzufügen:

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

Fügen Sie dann diesen Eintrag zu crontab hinzu:

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

Der letzte Tag des Monats kann zwischen 28 und 31 liegen, je nachdem, um welchen Monat es sich handelt (Februar, März usw.). In beiden Fällen ist der nächste Tag jedoch immer der 1. des nächsten Monats. Damit können wir sicherstellen, dass wir immer am letzten Tag eines Monats einen Job ausführen, indem wir den folgenden Code verwenden:

0 8 28-31 * * [ "$(date +%d -d tomorrow)" = "01" ] && /your/script.sh
Rakesh Chintha
quelle