Como escrever uma expressão cron: cinco campos e a regra OU que ninguém menciona
Publicado a 21/05/2026 · 10 min de leitura · Ferramentas para programadores
Daniel Okonkwo — Programador front-end e redator de Tecnologia na Allin
Desempenho web · Formatos de ficheiro
Verificado a partir de 2 fontes
Uma expressão cron são cinco campos separados por espaços, lidos da esquerda para a direita: minuto (0-59), hora (0-23), dia do mês (1-31), mês (1-12 ou nomes de três letras) e dia da semana (0-6 em POSIX com 0 para domingo, alargado a 0-7 pela maioria das implementações de modo que 7 também é domingo). Um asterisco designa todos os valores do intervalo daquele campo. Um hífen forma um intervalo inclusivo: 8-11 no campo da hora é 8, 9, 10 e 11. Uma vírgula forma uma lista, e listas e intervalos misturam-se: 1-3,7-9. Uma barra acrescenta um passo, e é aí que se erra à primeira — um passo percorre um intervalo, não define uma cadência: */15 no campo dos minutos é 0, 15, 30 e 45, e */40 é 0 e 40 e depois nada até a hora seguinte reiniciar o intervalo. A segunda armadilha, maior, é que o dia do mês e o dia da semana se combinam com OU e não com E. Se ambos estiverem restringidos, a tarefa corre quando qualquer um dos dois corresponder. Portanto 0 0 1 * 1 não significa a primeira segunda-feira do mês: dispara à meia-noite do dia 1 de cada mês E à meia-noite de todas as segundas-feiras. Para fixar uma tarefa a um dia da semana concreto, deixe o dia do mês como asterisco, e vice-versa. Tudo o resto que encontrar — @daily, um sexto campo de segundos, o carácter ?, L, W e # — está fora da norma e depende inteiramente do cron que está a correr.
Minuto, hora, dia do mês, mês, dia da semana. As armadilhas: um passo é um avanço dentro de um intervalo e não um período, e os dois campos de dia combinam-se com OU — pelo que 0 0 1 * 1 dispara no dia 1 e em todas as segundas-feiras.
Cinco campos, e um passo não é uma cadência
Leia os campos da esquerda para a direita e a notação cabe na cabeça. O asterisco representa todo o intervalo do seu campo. O hífen forma um intervalo inclusivo: 8-11 no campo da hora são quatro horas, não três. A vírgula forma uma lista, e listas e intervalos combinam-se livremente no mesmo campo: 1-3,7-9 é um conjunto legal de seis valores. Os nomes podem substituir os números nos campos do mês e do dia da semana, com três letras, embora o cron Vixie clássico não aceite intervalos nem listas construídas com nomes — 1-3 é interpretado onde JAN-MAR pode falhar, boa razão para ficar pelos números em tudo o que deva ser portável.
A barra é onde o modelo mental parte. Um passo percorre um intervalo, não é uma cadência de repetição, e o intervalo recomeça no início de cada unidade superior. No campo dos minutos, */15 percorre 0, 15, 30, 45, depois a hora vira e recomeça em 0 — o que calha ser exatamente de quinze em quinze minutos, pelo que ninguém nota a diferença. Escreva */40 e a ilusão desaba: percorre 0 e 40, depois a hora vira e recomeça em 0, dando intervalos de quarenta minutos e depois de vinte. O mesmo para um intervalo limitado: 0-23/2 no campo da hora são as horas pares, e 1-9/2 são 1, 3, 5, 7 e 9. Se precisar mesmo de uma cadência que não divide a sua unidade, o cron não a consegue exprimir numa linha: precisa de duas entradas ou de uma guarda dentro do comando.
Os dois campos de dia combinam-se com OU, não com E
Esta é a regra que quase todas as explicações erram, e não é uma esquisitice de uma implementação: está escrita na especificação. A POSIX estabelece que, se o dia do mês for dado como elemento ou lista e o dia da semana também, então qualquer dia que corresponda ao dia do mês ou ao dia da semana é considerado. O manual crontab(5) diz o mesmo em palavras mais simples e dá o exemplo canónico: 30 4 1,15 * 5 corre às 4h30 da manhã no dia 1 e no dia 15 de cada mês, mais todas as sextas-feiras. Não o dia 1 ou o 15 quando calham a uma sexta. Ambos os conjuntos, reunidos.
As regras práticas que daí decorrem são curtas. Se exatamente um dos dois campos de dia for um asterisco, manda o outro e não há ambiguidade — é o caso que deve escrever quase sempre. Se ambos forem asteriscos, todos os dias correspondem, o que também é inequívoco. Só quando ambos estão restringidos é que a união entra em jogo, e o resultado quase nunca é o pretendido: 0 0 1 * 1 dispara cerca de cinco vezes por mês em vez de uma. Não existe sintaxe cron para a interseção, portanto se precisar mesmo da primeira segunda-feira do mês, o truque padrão é agendar a união e estreitá-la dentro do comando — correr em 0 0 1-7 * 1 e abrir o script com um teste de que o dia da semana é segunda, ou em 0 0 * * 1 com um teste de que o dia do mês é 7 ou menos. Os cron de estilo Quartz acrescentam um operador # para isto, mas não está disponível no cron do sistema.
Tudo o que vai além dos cinco campos depende da implementação
A POSIX define cinco campos, o asterisco, os intervalos, as listas e mais nada. Cada comodidade que tenha visto para lá disso é uma extensão, e as extensões variam. O passo com barra é uma extensão Vixie hoje quase universal mas ainda ausente da norma. As cadeias abreviadas — @yearly e o seu sinónimo @annually, @monthly, @weekly, @daily e o seu sinónimo @midnight, @hourly, e @reboot — vêm da mesma linhagem, e @reboot em particular não tem significado fixo entre sistemas, porque o que conta como reinício depende do serviço. Um sexto campo para os segundos é comum em agendadores de aplicação como Quartz, Spring e várias bibliotecas Node, e ausente do cron de sistema: uma expressão copiada da documentação de uma framework para um crontab ficará desfasada de um campo e agendará outra coisa por completo em vez de falhar de forma ruidosa.
O ponto de interrogação pertence à mesma família. No Quartz significa nenhum valor específico e existe precisamente para resolver a ambiguidade entre dia do mês e dia da semana declarando um campo irrelevante; o cron de sistema não o aceita de todo. Os operadores L, W e # para último dia do mês, dia útil mais próximo e enésimo dia da semana são igualmente exclusivos do Quartz. Dois factos do ambiente contam mais do que toda esta sintaxe: o serviço executa as tarefas no fuso horário para que está configurado, pelo que uma expressão correta numa instalação pode disparar uma hora antes ou depois noutra, e numa mudança de hora uma tarefa agendada na hora suprimida pode não correr de todo, enquanto uma na hora repetida pode correr duas vezes. E o cron é um gatilho, não um executor de tarefas — sem repetições, sem controlo de concorrência e sem memória de que a execução anterior falhou. Se duas invocações não podem sobrepor-se, adquira por si mesmo um bloqueio dentro do comando.
| Posição | Campo | Valores permitidos | O erro que convida |
|---|---|---|---|
| 1.º | Minuto | 0-59 | Escrever * aqui quando queria 0 — uma expressão como * 3 * * * corre sessenta vezes, a cada minuto da hora das 3 da manhã |
| 2.º | Hora | 0-23 | Raciocinar em relógio de 12 horas: não existe 24 nem pm, meia-noite é 0 e as 11 da noite são 23 |
| 3.º | Dia do mês | 1-31 | Usar 31 esperando doze execuções por ano: os meses mais curtos simplesmente nunca correspondem, pelo que a tarefa salta em silêncio fevereiro, abril, junho, setembro e novembro |
| 4.º | Mês | 1-12, ou JAN a DEC | Supor que os nomes funcionam em todo o lado: o cron Vixie clássico aceita nomes de três letras mas rejeita intervalos ou listas deles, pelo que JAN-MAR pode não ser analisado onde 1-3 é |
| 5.º | Dia da semana | 0-6 em POSIX com 0 para domingo; 0-7 na maioria das implementações, onde 7 também é domingo | Restringir este campo e o dia do mês ao mesmo tempo: os dois combinam-se com OU, pelo que a tarefa dispara nos dois conjuntos de dias e não na sua interseção |
Perguntas frequentes
- O que significa exatamente */15?
- No campo dos minutos seleciona os valores 0, 15, 30 e 45 — um passo de quinze através do intervalo 0 a 59, a começar no primeiro valor do intervalo. Não é um temporizador que dispara quinze minutos depois da última execução, e não sobrevive a uma divisão inexata: */40 no campo dos minutos seleciona apenas 0 e 40, pelo que os intervalos são de quarenta minutos e depois de vinte quando a hora seguinte reinicia o intervalo. Se quiser uma cadência medida desde a última execução e não desde o início da hora, o cron é a ferramenta errada: um temporizador systemd com OnUnitActiveSec, ou um ciclo dentro de um processo de longa duração, é a certa.
- Como corro uma tarefa a cada 90 minutos?
- Não numa só linha, porque 90 não divide 60 e um passo fica confinado a um único campo. O padrão repete-se, porém, de três em três horas, pelo que duas entradas o cobrem exatamente: 0 */3 * * * dá as 00:00, 03:00, 06:00 e assim por diante, e 30 1-23/3 * * * dá as 01:30, 04:30, 07:30 e assim por diante. Juntas produzem uma execução a cada noventa minutos, e a sequência fecha de forma limpa à meia-noite porque vinte e quatro horas são exatamente dezasseis intervalos de noventa minutos. O mesmo truque de duas entradas serve para qualquer cadência que divida um número inteiro de horas; o que não o faz, como a cada 50 minutos, não pode ser expresso em cron de todo.
- Porque é que a minha tarefa noturna correu duas vezes, ou nenhuma?
- A causa habitual é uma mudança de hora. O cron compara a hora do relógio de parede: quando o relógio avança, uma hora que nunca existiu nunca é cumprida e uma tarefa agendada lá dentro simplesmente não corre; quando o relógio recua, uma hora ocorre duas vezes e uma tarefa agendada lá dentro pode disparar duas vezes. As implementações diferem no esforço de compensação, e por isso o mesmo crontab comporta-se de forma diferente em duas distribuições. As correções fiáveis são agendar as tarefas sensíveis fora da janela de transição, correr o serviço num fuso sem mudanças de hora, ou tornar o próprio comando seguro para correr duas vezes. Esta última compensa de qualquer forma: a idempotência protege-o também de repetições, execuções sobrepostas e relançamentos manuais.
Artigos que podem interessar-lhe
Todos os guias →Ferramentas relacionadas
Fontes
Detetaste um erro neste artigo?