Ir al contenido
Allin

Cómo escribir una expresión cron: cinco campos y la regla OR que nadie menciona

Publicado el 21/5/2026 · 10 min de lectura · Herramientas para desarrolladores

Daniel Okonkwo

Daniel OkonkwoDesarrollador front-end y redactor de Tecnología en Allin

Rendimiento web · Formatos de archivo

Verificado con 2 fuentes

Ver perfil
En resumen

Una expresión cron son cinco campos separados por espacios, leídos de izquierda a derecha: minuto (0-59), hora (0-23), día del mes (1-31), mes (1-12 o nombres de tres letras) y día de la semana (0-6 en POSIX con 0 como domingo, ampliado a 0-7 por casi todas las implementaciones de modo que 7 también es domingo). Un asterisco significa todos los valores del rango de ese campo. Un guion forma un rango inclusivo: 8-11 en el campo de la hora es 8, 9, 10 y 11. Una coma forma una lista, y listas y rangos se mezclan: 1-3,7-9. Una barra añade un paso, y ahí es donde se falla la primera vez — un paso recorre un rango, no fija un intervalo: */15 en el campo de los minutos es 0, 15, 30 y 45, y */40 es 0 y 40 y luego nada hasta que la hora siguiente reinicia el rango. La segunda trampa, mayor, es que el día del mes y el día de la semana se combinan con OR y no con AND. Si ambos están restringidos, la tarea se ejecuta cuando cualquiera de los dos coincide. Así que 0 0 1 * 1 no significa el primer lunes del mes: se dispara a medianoche del día 1 de cada mes Y a medianoche de todos los lunes. Para fijar una tarea a un día de la semana concreto, deja el día del mes como asterisco, y al revés. Todo lo demás que verás — @daily, un sexto campo de segundos, el carácter ?, L, W y # — queda fuera del estándar y depende por completo del cron que estés ejecutando.

Minuto, hora, día del mes, mes, día de la semana. Las trampas: un paso es un avance dentro de un rango y no un intervalo, y los dos campos de día se combinan con OR — así que 0 0 1 * 1 se dispara el día 1 y todos los lunes.

Cinco campos, y un paso no es un intervalo

Lee los campos de izquierda a derecha y la notación cabe en la cabeza. El asterisco representa todo el rango de su campo. El guion forma un rango inclusivo: 8-11 en el campo de la hora son cuatro horas, no tres. La coma forma una lista, y listas y rangos se combinan libremente en el mismo campo: 1-3,7-9 es un conjunto legal de seis valores. Los nombres pueden sustituir a los números en los campos de mes y día de la semana, con tres letras, aunque el cron Vixie clásico no acepta rangos ni listas construidas con nombres — 1-3 se analiza donde JAN-MAR puede fallar, buena razón para quedarse con números en todo lo que deba ser portable.

La barra es donde se rompe el modelo mental. Un paso recorre un rango, no es un intervalo de repetición, y el rango vuelve a empezar al inicio de cada unidad superior. En el campo de los minutos, */15 recorre 0, 15, 30, 45, luego cambia la hora y empieza otra vez en 0 — que resulta ser justo cada quince minutos, así que nadie nota la diferencia. Escribe */40 y la ilusión se derrumba: recorre 0 y 40, luego cambia la hora y vuelve a 0, dando huecos de cuarenta minutos y después de veinte. Lo mismo con un rango acotado: 0-23/2 en el campo de la hora son las horas pares, y 1-9/2 son 1, 3, 5, 7 y 9. Si de verdad necesitas un intervalo que no divide su unidad, cron no puede expresarlo en una línea: necesitas dos entradas o una guarda dentro del comando.

Los dos campos de día se combinan con OR, no con AND

Esta es la regla que casi todas las explicaciones fallan, y no es una rareza de una implementación: está escrita en la especificación. POSIX establece que si el día del mes se da como elemento o lista y el día de la semana también, entonces se toma cualquier día que coincida con el día del mes o con el día de la semana. El manual crontab(5) dice lo mismo en palabras más llanas y da el ejemplo canónico: 30 4 1,15 * 5 se ejecuta a las 4:30 de la madrugada del día 1 y del 15 de cada mes, más todos los viernes. No el 1 o el 15 cuando caen en viernes. Ambos conjuntos, unidos.

Las reglas prácticas que se siguen son breves. Si exactamente uno de los dos campos de día es un asterisco, manda el otro y no hay ambigüedad — es el caso que deberías escribir casi siempre. Si ambos son asteriscos, coinciden todos los días, lo que también es inequívoco. Solo cuando ambos están restringidos entra en juego la unión, y el resultado casi nunca es el pretendido: 0 0 1 * 1 se dispara unas cinco veces al mes en lugar de una. No existe sintaxis cron para la intersección, así que si de verdad necesitas el primer lunes del mes, el truco estándar es programar la unión y estrecharla dentro del comando — lanzarlo con 0 0 1-7 * 1 y abrir el script con una prueba de que el día de la semana es lunes, o con 0 0 * * 1 y una prueba de que el día del mes es 7 o menos. Los cron de estilo Quartz añaden un operador # para esto, pero no está disponible en el cron del sistema.

Todo lo que va más allá de los cinco campos depende de la implementación

POSIX define cinco campos, el asterisco, los rangos, las listas y nada más. Cada comodidad que hayas visto más allá es una extensión, y las extensiones varían. El paso con barra es una extensión de Vixie hoy casi universal pero todavía ausente del estándar. Las cadenas abreviadas — @yearly y su sinónimo @annually, @monthly, @weekly, @daily y su sinónimo @midnight, @hourly, y @reboot — vienen de la misma estirpe, y @reboot en particular no tiene un significado fijo entre sistemas, porque qué cuenta como reinicio depende del demonio. Un sexto campo para los segundos es común en planificadores de aplicación como Quartz, Spring y varias bibliotecas de Node, y ausente del cron del sistema: una expresión copiada de la documentación de un framework a un crontab quedará desplazada un campo y programará algo completamente distinto en vez de fallar de forma ruidosa.

El signo de interrogación pertenece a la misma familia. En Quartz significa ningún valor específico y existe precisamente para resolver la ambigüedad entre día del mes y día de la semana declarando irrelevante uno de los campos; el cron del sistema no lo acepta en absoluto. Los operadores L, W y # para último día del mes, día laborable más cercano y enésimo día de la semana son también exclusivos de Quartz. Dos hechos del entorno importan más que toda esta sintaxis: el demonio ejecuta las tareas en la zona horaria para la que está configurado, así que una expresión correcta en un despliegue puede dispararse una hora antes o después en otro, y en un cambio de hora una tarea programada en la hora suprimida puede no ejecutarse en absoluto mientras una en la hora repetida puede ejecutarse dos veces. Y cron es un disparador, no un ejecutor de tareas — sin reintentos, sin control de concurrencia y sin memoria de que la ejecución anterior falló. Si dos invocaciones no deben solaparse, toma tú mismo un bloqueo dentro del comando.

Los cinco campos, en orden, con el error que cada uno invita
PosiciónCampoValores permitidosEl error que invita
1.ºMinuto0-59Escribir * aquí cuando querías 0 — una expresión como * 3 * * * se ejecuta sesenta veces, cada minuto de las 3 de la madrugada
2.ºHora0-23Razonar en reloj de 12 horas: no existe el 24 ni el pm, medianoche es 0 y las 11 de la noche son 23
3.ºDía del mes1-31Usar 31 esperando doce ejecuciones al año: los meses más cortos sencillamente nunca coinciden, así que la tarea se salta en silencio febrero, abril, junio, septiembre y noviembre
4.ºMes1-12, o JAN a DECSuponer que los nombres funcionan en todas partes: el cron Vixie clásico acepta nombres de tres letras pero rechaza rangos o listas de ellos, así que JAN-MAR puede no analizarse donde 1-3 sí
5.ºDía de la semana0-6 en POSIX con 0 como domingo; 0-7 en casi todas las implementaciones, donde 7 también es domingoRestringir este campo y el día del mes a la vez: ambos se combinan con OR, así que la tarea se dispara en los dos conjuntos de días y no en su intersección
Generador de expresión cronConstruye una programación cron campo a campo — minuto, hora, día del mes, mes, día de semana — y ve la expresión y su descripción en lenguaje claro actualizarse en vivo. Los preajustes cubren los casos comunes y la sintaxis (*, */5, 1-5, 1,15) se explica.Probar la herramienta

Preguntas frecuentes

¿Qué significa exactamente */15?
En el campo de los minutos selecciona los valores 0, 15, 30 y 45 — un paso de quince a través del rango 0 a 59, empezando por el primer valor del rango. No es un temporizador que se dispare quince minutos después de la última ejecución, y no sobrevive a una división inexacta: */40 en el campo de los minutos selecciona solo 0 y 40, así que los huecos son de cuarenta minutos y luego de veinte cuando la hora siguiente reinicia el rango. Si quieres un intervalo medido desde la última ejecución y no desde el inicio de la hora, cron es la herramienta equivocada: un temporizador de systemd con OnUnitActiveSec, o un bucle dentro de un proceso de larga vida, es la correcta.
¿Cómo ejecuto una tarea cada 90 minutos?
No en una sola línea, porque 90 no divide a 60 y un paso está confinado a un único campo. El patrón sí se repite cada tres horas, así que dos entradas lo cubren exactamente: 0 */3 * * * da las 00:00, 03:00, 06:00 y así sucesivamente, y 30 1-23/3 * * * da las 01:30, 04:30, 07:30 y así sucesivamente. Juntas producen una ejecución cada noventa minutos, y la secuencia cierra limpiamente a medianoche porque veinticuatro horas son exactamente dieciséis intervalos de noventa minutos. El mismo truco de dos entradas sirve para cualquier intervalo que divida un número entero de horas; lo que no lo hace, como cada 50 minutos, no puede expresarse en cron en absoluto.
¿Por qué mi tarea nocturna se ejecutó dos veces, o ninguna?
La causa habitual es un cambio de hora. Cron compara la hora del reloj de pared: cuando el reloj se adelanta, una hora que nunca existió nunca se cumple y una tarea programada dentro de ella sencillamente no se ejecuta; cuando el reloj se atrasa, una hora ocurre dos veces y una tarea programada dentro puede dispararse dos veces. Las implementaciones difieren en cuánto se esfuerzan por compensarlo, y por eso un mismo crontab se comporta distinto en dos distribuciones. Las correcciones fiables son programar las tareas sensibles fuera de la ventana de transición, ejecutar el demonio en una zona horaria sin cambios de hora, o hacer que el propio comando sea seguro de ejecutar dos veces. Esto último conviene igualmente: la idempotencia te protege también de reintentos, ejecuciones solapadas y relanzamientos manuales.

Artículos que podrían interesarte

Todas las guías
ExplicaciónCómo funcionan los permisos de archivo en Unix: leer 755 sin adivinarLectura 4, escritura 2, ejecución 1, y cada uno de los tres dígitos describe a una parte distinta. Lo que casi todas las explicaciones fallan es qué hace el bit de ejecución en un directorio: concede el paso, no el derecho a ejecutar nada.GuíaCódigos de estado HTTP explicados: los que de verdad se confunden301 frente a 308, 302 frente a 307, 401 frente a 403, 404 frente a 410 — y qué promete de verdad Retry-After en un 429 o un 503. Los pares donde elegir mal el código cambia el comportamiento, no solo la redacción.Explicación¿Qué es una expresión cron?Una expresión cron programa una tarea para ejecutarse automáticamente a horas fijadas. Aquí tienes para qué sirve, sus cinco campos, cómo leerla y los fallos comunes.TutorialCómo escribir un robots.txt: directivas, coincidencia y lo que no puede ocultarCuatro directivas, dos comodines, un archivo en la raíz del host. Es una instrucción de rastreo y nada más: no retira una página de los resultados, no restringe el acceso y publica cada ruta que enumeras en él.ExplicaciónLo que un porcentaje de disponibilidad permite en realidadTres nueves suena a promesa hasta que lo divides en minutos. Lo que compra un 99,9 % al año, al mes, a la semana y al día; por qué la ventana de medición importa mucho más que el nueve de más; y los dos meses distintos que esta herramienta usa para un mismo identificador.ExplicaciónLa hora dorada y la hora azul son ángulos, no horasLa hora dorada va de +6° a −4° de altura del sol y la hora azul de −4° a −6°, por lo que dura cuarenta minutos en el ecuador, más de una hora en latitudes medias, y por encima de 72,6° en junio no ocurre en absoluto. Los umbrales de esta calculadora, comprobados sobre su propia salida.

Herramientas relacionadas

Fuentes

¿Has detectado un error en este artículo?