Cómo leer una expresión cron

Los cinco campos de cron explicados, con rangos, saltos, listas, nombres y atajos, además de la regla del día del mes y el día de la semana que pilla a todo el mundo.

Una expresión cron son cinco valores separados por espacios, que se leen de izquierda a derecha como minuto, hora, día del mes, mes y día de la semana. La tarea se ejecuta cuando la hora actual coincide con todos los campos.

30 4 * * 1
│  │ │ │ │
│  │ │ │ └── day of week (1 = Monday)
│  │ │ └──── month (any)
│  │ └────── day of month (any)
│  └──────── hour (4am)
└─────────── minute (30)

Esa se lee como «a las 04:30 todos los lunes».

Los cinco campos

#CampoValores permitidosNotas
1Minuto0 a 59
2Hora0 a 23Reloj de 24 horas, 0 es medianoche
3Día del mes1 a 31
4Mes1 a 12, o de JAN a DEC
5Día de la semana0 a 6, o de SUN a SAT0 es domingo, y la mayoría de implementaciones de cron también aceptan 7

Algunos planificadores usan otra forma. Quartz, que utiliza mucho software en Java, pone los segundos delante, añade un año opcional y numera los días de la semana del 1 al 7 empezando por el domingo. Si una expresión tiene seis o siete campos, comprueba para qué sistema se escribió antes de fiarte de tu lectura.

Los operadores

Hay cuatro, y se combinan dentro de un mismo campo.

SímboloNombreEjemploSignificado
*Cualquiera* * * * *Cada minuto de cada día
,Lista0 9,13,17 * * *A las 09:00, 13:00 y 17:00
-Rango0 9-17 * * *Cada hora de 09:00 a 17:00, ambas incluidas
/Salto*/15 * * * *En los minutos :00, :15, :30 y :45

Un salto se aplica siempre sobre un rango. */15 en el campo de minutos significa «empezando en 0, toma un valor de cada 15 hasta el 59». También puedes aplicar un salto a un rango explícito: 5-30/10 da 5, 15 y 25, y luego se detiene porque 35 está más allá del final.

Los saltos no significan «cada 15 minutos a partir de ahora»; seleccionan valores fijos del reloj. */40 se dispara en :00 y en :40, y después solo espera 20 minutos hasta el :00 de la hora siguiente, porque la cuenta se reinicia cada hora. Las listas pueden contener rangos y saltos, así que 0 0-6/2,12,18-23 * * * es un campo de hora válido.

Nombres y atajos

Los campos de mes y de día de la semana aceptan nombres de tres letras, y las mayúsculas dan igual, así que JAN, jan y Jan son lo mismo. La documentación clásica de crontab dice que no se permiten rangos ni listas de nombres, aunque muchas implementaciones modernas sí aceptan MON-FRI. Si no tienes claro qué ejecuta tu tarea, usa números: 1-5 no es ambiguo en ninguna parte.

Hay varios atajos que sustituyen a la expresión completa de cinco campos:

AtajoEquivalenteCuándo se ejecuta
@yearly o @annually0 0 1 1 *Medianoche del 1 de enero
@monthly0 0 1 * *Medianoche del día 1 de cada mes
@weekly0 0 * * 0Medianoche del domingo
@daily o @midnight0 0 * * *Medianoche de cada día
@hourly0 * * * *En punto, cada hora

También existe @reboot, pero no es una programación: ejecuta la tarea una vez, cuando arranca el propio cron.

La trampa del día del mes y el día de la semana

Esta es la que muerde. Cuando el campo del día del mes y el del día de la semana están los dos restringidos, es decir, ninguno de los dos es *, cron ejecuta la tarea cuando coincide cualquiera de los dos, no cuando coinciden los dos.

0 0 13 * 5

Podrías leerlo como «medianoche del viernes 13». En realidad significa «medianoche del día 13 de cada mes, y además medianoche de todos los viernes»: más de sesenta ejecuciones al año en lugar de una o dos.

El comportamiento de Y que esperabas se aplica solo cuando uno de los dos campos es *. Así que 0 0 * * 5 es todos los viernes y 0 0 13 * * es todos los días 13, los dos perfectamente normales. Lo que dispara el O es restringir los dos.

No hay una manera estándar de expresar «viernes 13» en cinco campos. La solución habitual es programarlo para todos los días 13 y hacer que la tarea compruebe el día de la semana antes de hacer nada.

Un matiz relacionado: algunas implementaciones deciden si un campo está restringido comprobando si empieza literalmente por *. Con esas, un 0-6 en el campo del día de la semana cuenta como restringido aunque cubra todos los días, y activa el comportamiento de O sin decir nada. Usa * cuando quieras decir «cualquiera».

Errores que conviene comprobar

  • * */2 * * * se ejecuta cada minuto durante una hora de cada dos: 720 ejecuciones al día. Lo que querías era 0 */2 * * *, que son 12.
  • Si falta el campo de minutos, todo se desplaza una posición a la izquierda y normalmente se sigue analizando sin error, lo que te da una programación válida a una hora completamente equivocada.
  • 0 0 31 * * se salta sin avisar los meses que no tienen día 31.
  • Cron usa la zona horaria del sistema o del usuario, salvo que el planificador te deje fijar una. Cuando los relojes cambian por el horario de verano, una tarea que caiga en la hora que se salta puede no ejecutarse en absoluto, y una que caiga en la hora que se repite puede ejecutarse dos veces, según la implementación. Las tareas que tienen que ejecutarse una vez al día están más seguras si se programan lejos de la madrugada, que es donde cae el cambio.

Ante la duda, lee la expresión como si fuera una frase y luego comprueba las siguientes veces en las que se dispararía de verdad. Si esas fechas no son las que describiste, la equivocada es la expresión, no tu lectura.