Programe el último día de cada mes

10

Leí de una instrucción para programar un guión el último día del mes:

Nota:
El lector astuto podría estar preguntándose cómo podría configurar un comando para que se ejecute el último día de cada mes porque no puede configurar el valor de dayofmonth para cubrir cada mes. Este problema ha afectado a los programadores de Linux y Unix, y ha generado varias soluciones diferentes. Un método común es agregar una instrucción if-then que use el comando date para verificar si la fecha de mañana es 01:

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

Esto verifica todos los días a las 12 del mediodía para ver si es el último día del mes, y si es así, cron ejecuta el comando.

ingrese la descripción de la imagen aquí

Como [`date +%d -d tomorrow` = 01 ]funciona
¿Es correcto declarar then; command1?

Cálculo
fuente
¿Estás seguro de que eso es literal lo que dice? Como está escrito aquí, de hecho no funciona.
Michael Homer
Publiqué la instantánea. @ MichaelHomer
Cálculo
¡Gracias! He jugueteado con el formato para que coincida, todavía no está del todo bien, pero es lo que dice la imagen.
Michael Homer
1
Falta ; endif?
danblack
No funciona Contiene errores de sintaxis: sin espacio después [y no fial final. Además, %es especial en crontabs.
Kusalananda

Respuestas:

17

Resumen

El código correcto debería ser:

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

Llame a este script end_of_month.shy la llamada en cron es simplemente:

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

Eso ejecutaría el script end_of_month(que verificará internamente que el día sea el último día del mes) solo los días 28, 29, 30 y 31. No hay necesidad de verificar el final del mes en ningún otro día.

Post antiguo

Esa es una cita del libro "Linux Command Line and Shell Scripting Bible" de Richard Blum, Christine Bresnahan pp 442, Tercera edición, John Wiley & Sons © 2015.

Sí, eso es lo que dice, pero eso está mal / incompleto:

  • Falta un cierre fi.
  • Necesita espacio entre [y lo siguiente `.
  • Se recomienda encarecidamente usar $ (...) en lugar de `…`.
  • Es importante que use comillas alrededor de expansiones como"$(…)"
  • Hay un adicional ;despuésthen

¿Cómo puedo saber? (bueno, por experiencia ☺) pero puedes probar Shellcheck . Pegue el código del libro (después de los asteriscos) y le mostrará los errores enumerados anteriormente más un "shebang faltante". Un script sin ningún error en Shellcheck es este:

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

Ese sitio funciona porque lo que se escribió es "código shell". Esa es una sintaxis que funciona en muchos shells.

Algunos problemas que Shellcheck no menciona son:

  • Se supone que el comando date es la versión de fecha GNU. El que tiene una -dopción que acepta tomorrowcomo valor (busybox tiene una opción -d pero no comprende mañana y BSD tiene una -dopción pero no está relacionada con la "visualización" de la hora).

  • Es mejor configurar el formato después de todas las opciones date -d tomorrow +'%d'.

  • La hora de inicio del cron siempre es la hora local, lo que puede hacer que un trabajo comience 1 hora antes o después de un recuento exacto de días si el horario de verano (horario de verano) se configuró o no.

Lo que hicimos es un script de shell que podría llamarse con cron. Podemos modificar aún más el script para aceptar argumentos del programa o comando a ejecutar, de esta manera (finalmente, el código correcto):

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

Llame a este script end_of_month.shy la llamada en cron es simplemente:

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

Eso ejecutaría el script end_of_month(que verificará internamente que el día sea el último día del mes) solo los días 28, 29, 30 y 31. No hay necesidad de verificar el final del mes en ningún otro día.

Asegúrese de que se incluya la ruta correcta. La RUTA dentro del cron no será (probablemente) igual que la RUTA del usuario.

Tenga en cuenta que hay una secuencia de comandos de fin de mes probada (como se indica a continuación) que podría llamar a muchas otras utilidades o secuencias de comandos.

Esto también evitará el problema adicional que cron genera con la línea de comando completa:

  • Cron divide la línea de comando en cualquiera, %incluso si se cita con 'o "(solo \funciona aquí). Esa es una forma común en la que fallan los trabajos cron.

Puede probar si el end_of_month.shscript funciona correctamente en alguna fecha (sin esperar hasta el final del mes para descubrir que no funciona) probándolo con faketime:

$ faketime 2018/10/31 ./end_of_month echo "Command will be executed...."
Command will be executed....
Isaac
fuente
ast-open date(o el dateincorporado de ksh93 si ksh93 fue construido como parte de ast-open) admite date -d tomorrow +%so date +%s tomorrow).
Stéphane Chazelas
2
La experiencia ha demostrado (muchas veces) que es mucho mejor probar el script que se ejecuta correctamente con faketime que esperar hasta fin de mes para descubrir que el trabajo programado no funcionó. Como descubrir que * * * * * echo "$(date -u +'date %c')" >>~/testfileno funcionará porque uno olvidó citar el \%(que se vuelve difícil de depurar si solo es posible un intento cada mes). @Kusalananda
Isaac
8

Suponiendo que los errores de sintaxis son fijos, y el comando se reformuló ligeramente para ser menos detallado:

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

Esto se ejecuta date +%d -d tomorrow(suponiendo que se dateutilice GNU ) para obtener la fecha de mañana como un número de dos dígitos. Si el número no es 01, entonces hoy no es el último día del mes. En ese caso, las pruebas tiene éxito y command1se no ejecutados. El trabajo se ejecuta al mediodía en los días que posiblemente podrían ser el último día del mes.

El comando original:

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

Esto tiene algunos problemas:

  • No hay espacio después [.
  • A ;directamente después then.
  • %es especial en las especificaciones de trabajo cron y debe escaparse como \%(ver man 5 crontab).
  • No hay final fial final que coincida con el if.
Kusalananda
fuente
2
El problema con el uso en [ ... ] && command1lugar de if...es que en los días que no son el último día del mes, el trabajo cron finalizará con un estado de salida distinto de cero y es posible que deba informarse de ese error . Usar [ "$(...)" != 01 ] || command1es otra forma de evitar el problema.
Stéphane Chazelas
1
Otro problema con el código original es ;entre theny command1.
Stéphane Chazelas
@ StéphaneChazelas Otra razón por la que no me gustan las frases sencillas es que son difíciles de leer.
Kusalananda
La diferencia horaria entre ejecuciones consecutivas del comando podría no ser una cantidad entera de (24 horas) días, ya que un cambio de horario de verano cambiará la hora de inicio de cron.
Isaac
3
@cat No importa cómo cite, ya sea "o '(excepto \), el signo de porcentaje %hará que cron rompa la línea en dos partes. Esa es una forma habitual de hacer que cron falle.
Isaac
-1

Para programar el último día de cada mes, puede intentar: 0 0 15,L * *.

Kylin Sky
fuente