Trabajo CRON para ejecutar el último día del mes

95

Necesito crear un trabajo CRON que se ejecutará el último día de cada mes. Lo crearé usando cPanel.

Se agradece cualquier ayuda. Gracias

Utku Dalmaz
fuente

Respuestas:

180

Posiblemente, la forma más fácil es simplemente hacer tres trabajos separados:

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

Sin embargo, eso se ejecutará el 28 de febrero, incluso en años bisiestos, por lo que, si eso es un problema, deberá encontrar otra forma.


Sin embargo, generalmente es mucho más fácil y correcto ejecutar el trabajo lo antes posible el primer día de cada mes, con algo como:

0 0 1 * * myjob.sh

y modificar el script para procesar los datos del mes anterior .

Esto elimina cualquier molestia que pueda encontrar al determinar qué día es el último del mes y también garantiza que todos los datos de ese mes estén disponibles, suponiendo que esté procesando datos. Correr cinco minutos antes de la medianoche del último día del mes puede hacer que se pierda todo lo que suceda entre esa hora y la medianoche.

Esta es la forma habitual de hacerlo de todos modos, para la mayoría de los trabajos de fin de mes.


Si todavía realmente desea que se ejecute en el último día del mes, una opción es simplemente detectar si mañana es el primero (ya sea como parte de su script, o en el crontab en sí).

Entonces, algo como:

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

debería ser un buen comienzo, asumiendo que tiene un dateprograma relativamente inteligente .

Si su dateprograma no está lo suficientemente avanzado como para darle fechas relativas, puede armar un programa muy simple para darle el día del mes de mañana (no necesita toda la potencia de date), como por ejemplo:

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

y luego use (suponiendo que lo haya llamado tomdompara "el día de mañana del mes"):

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

Aunque es posible que desee considerar la adición de comprobación de errores, ya que ambos time()y mktime()puede regresar -1si algo va mal. El código anterior, por razones de simplicidad, no lo tiene en cuenta.

paxdiablo
fuente
7
sí, puede ser más fácil correr el primer día en lugar del último día :)
Utku Dalmaz
1
De hecho, el primer día del mes. Así es como se verá el código en PHP $ date = new DateTime ('2013-03-01'); $ fecha-> modificar ('- 1 mes'); $ mes anterior = $ fecha-> formato ('A-m'); // $ previousMonth ahora es 2013-02. Cree una consulta para buscar productos del mes anterior.
Lamy
Los datos del año bisiesto 29 de febrero se perderán, también debemos considerar eso. La respuesta de Thunder Rabbit a continuación considera eso, pero cron se ejecuta dos veces en febrero del año bisiesto
Hari Swaminathan
1
@Hari, la solución preferida sería ejecutar el primer día del mes y recopilar los datos del mes anterior. En ese caso, no faltaría el 29 de febrero.
paxdiablo
1
Teniendo en cuenta los años bisiestos: y si no te importa que se ejecute dos veces: 55 23 28,29 2 * myjob.sh
radiantRazor
52

Hay un método un poco más corto que se puede usar de manera similar a uno de los anteriores. Es decir:

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

Además, la entrada crontab podría actualizarse para verificar solo del 28 al 31, ya que no tiene sentido ejecutarla los otros días del mes. Lo que te daría:

0 23 28-31 * * [ $(date -d +1day +%d) -eq 1 ] && myscript.sh
Indie
fuente
No pude hacer que esto funcione como una entrada crontab (creo que algo debe escaparse). Sin embargo, funcionó bien en un script de shell llamado desde crontab. FYI, el error que obtuve fue /bin/sh: -c: line 1: unexpected EOF while looking for matching ')'.
Mark Rajcok
13
Esto funciona muy bien. En el archivo crontab, se debe escapar el%. Así que[ $(date -d +1day +\%d) -eq 1 ] && run_job
ColinM
¡Buen truco! Pero la pregunta fue etiquetada posixy la fecha POSIX no es compatible "-d +1day": - \ Una solución más complicada (y fea) sería:[ `date +\%d` -eq `cal | xargs echo | awk '{print $NF}'` ] && myscript.sh
ckujau
18

Configure un trabajo cron para que se ejecute el primer día del mes. Luego, cambie el reloj del sistema para que esté un día adelantado.

Tom Anderson
fuente
10
lo que significa que el reloj del sistema será incorrecto todo el tiempo. Lo siento, pero creo que esto causará más problemas. -1 entonces.
Rudy
69
Ahora sé cómo se sintió Galileo.
Tom Anderson
7
@iconoclast: Me gustaría pensar en ello más como un koan que como una broma.
Tom Anderson
14
Creo que esta es una idea realmente mala Y brillante. No hagan esto en casa, niños.
Pascal
11
@pascalbetz Oh, la gente puede hacerlo en casa, pero no debería hacerlo en el trabajo.
Tom Anderson
14

¿Qué pasa con este, después de Wikipedia?

55 23 L * * /full/path/to/command
Piohen
fuente
Bueno, ¿y eso? Eso: "errores de día del mes incorrectos en el archivo crontab, no se puede instalar. ¿Desea volver a intentar la misma edición?"
webjunkie
12
Para ser claros, esa entrada de wikipedia también menciona que "L" no es estándar.
sdupton
10

Adaptando la solución de paxdiablo, corro los días 28 y 29 de febrero. Los datos del 29 sobrescriben al 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
Conejo Trueno
fuente
4
A menos, por supuesto, que el trabajo tenga algún aspecto destructivo, como borrar todos los datos a medida que se procesan :-)
paxdiablo
En realidad, esta es una opción indolora (menos técnica), y es posible que no le importe que tres veces en 4 años obtendrá el cronjob demasiado pronto si simplemente omite el ,29.
Matt
@Matt: Umm, ¿no quiere decir que se ejecutará un día antes una vez cada cuatro años si su entrada crontab dice 55   23   28    2?
G-Man dice 'Reincorporar a Monica'
@ G-Man Sí, tienes razón, y además de eso, lo harás correr por segunda vez el día 29.
Matt
8

Puede configurar un trabajo cron para que se ejecute todos los días del mes y hacer que ejecute un script de shell como el siguiente. Este script determina si el número del día de mañana es menor que el de hoy (es decir, si mañana es un mes nuevo), y luego hace lo que quieras.

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
fuente
No estoy seguro de lo que gana al verificar si es menos que hoy; debería poder verificar si es el primero. A menos que el primer día del mes pueda ser el 2 o 3 :-)
paxdiablo
7

Para un método más seguro en un crontab basado en la solución @Indie (usar la ruta absoluta a date+ $()no funciona en todos los sistemas crontab):

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

Para la implementación de cron de AWS Cloudwatch (programación de Lambdas, etc.), esto funciona:

55 23 L * ? *

Se ejecuta a las 11:55 p. M. Del último día de cada mes.

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

Consulte una pregunta relacionada en el foro de unix.com.

eliminar
fuente
3

Puede conectar todas las respuestas en una línea cron y usar solo date comando.

Simplemente verifique la diferencia entre el día del mes que es hoy y mañana:

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

Si esta diferencia está por debajo de 0 significa que cambiamos el mes y hay último día del mes.

Lukasz Stelmach
fuente
3
55 23 28-31 * * echo "[ $(date -d +1day +%d) -eq 1 ] && my.sh" | /bin/bash 
Pato Donald
fuente
1
hacer eco del comando y canalizarlo a bash es la mejor manera cuando se trabaja con cron. Dado que cron está usando sh y no bash. Compruebe dónde está usando su bash 'which bash'. En FreeBSD es / usr / local / bin / bash, en Linux / bin / bash.
Donald Duck
2

¿Qué pasa con esto?

editar la .bashprofileadición del usuario :

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

Luego agregue esta entrada a crontab:

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

El último día del mes puede ser 28-31 dependiendo de qué mes sea (febrero, marzo, etc.). Sin embargo, en cualquiera de estos casos, el día siguiente es siempre el primero del mes siguiente. Entonces podemos usar eso para asegurarnos de que ejecutamos algún trabajo siempre el último día de un mes usando el siguiente código:

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