Como ler uma expressão cron

Os cinco campos do cron explicados, com intervalos, passos, listas, nomes e atalhos, mais a regra do dia do mês e do dia da semana que apanha toda a gente.

Uma expressão cron são cinco valores separados por espaços, lidos da esquerda para a direita como minuto, hora, dia do mês, mês e dia da semana. A tarefa corre quando a hora atual corresponde a todos os campos.

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

Esta lê-se "às 04:30 de todas as segundas-feiras".

Os cinco campos

#CampoValores permitidosNotas
1Minuto0 a 59
2Hora0 a 23Relógio de 24 horas, 0 é meia-noite
3Dia do mês1 a 31
4Mês1 a 12, ou JAN a DEC
5Dia da semana0 a 6, ou SUN a SAT0 é domingo, e a maioria das implementações de cron também aceita 7

Alguns agendadores usam outro formato. O Quartz, usado por muito software Java, põe os segundos à frente e acrescenta um ano opcional, e numera os dias da semana de 1 a 7 a começar no domingo. Se uma expressão tiver seis ou sete campos, verifique para que sistema foi escrita antes de confiar na sua leitura.

Os operadores

São quatro, e combinam-se dentro de um mesmo campo.

SímboloNomeExemploSignificado
*Qualquer* * * * *Todos os minutos de todos os dias
,Lista0 9,13,17 * * *Às 09:00, às 13:00 e às 17:00
-Intervalo0 9-17 * * *De hora a hora, das 09:00 às 17:00 inclusive
/Passo*/15 * * * *Aos :00, :15, :30 e :45

Um passo aplica-se sempre a um intervalo. */15 no campo dos minutos significa "a começar em 0, tomar cada 15.º valor até 59". Também se pode aplicar um passo a um intervalo explícito: 5-30/10 dá 5, 15 e 25, e depois para porque 35 já passou o fim.

Os passos não significam "de 15 em 15 minutos a partir de agora"; selecionam valores fixos do relógio. */40 dispara aos :00 e aos :40 e depois espera apenas 20 minutos pelos :00 da hora seguinte, porque a contagem recomeça a cada hora. As listas podem conter intervalos e passos, por isso 0 0-6/2,12,18-23 * * * é um campo de hora válido.

Nomes e atalhos

Os campos do mês e do dia da semana aceitam nomes de três letras, e a caixa não conta, por isso JAN, jan e Jan são a mesma coisa. A documentação clássica do crontab diz que intervalos e listas de nomes não são permitidos, embora muitas implementações modernas aceitem MON-FRI. Se não tiver a certeza de quem executa a sua tarefa, use números: 1-5 é inequívoco em todo o lado.

Vários atalhos substituem a expressão de cinco campos por inteiro:

AtalhoEquivalenteCorre
@yearly ou @annually0 0 1 1 *À meia-noite de 1 de janeiro
@monthly0 0 1 * *À meia-noite do dia 1 de cada mês
@weekly0 0 * * 0À meia-noite de domingo
@daily ou @midnight0 0 * * *À meia-noite, todos os dias
@hourly0 * * * *À hora certa

Também existe @reboot, mas não é um horário: corre a tarefa uma vez quando o próprio cron arranca.

A armadilha do dia do mês e do dia da semana

É esta que morde. Quando tanto o campo do dia do mês como o campo do dia da semana estão restringidos, ou seja, nenhum deles é *, o cron corre a tarefa quando qualquer um dos dois corresponde, e não quando os dois correspondem.

0 0 13 * 5

Isso pode ler-se como "à meia-noite da sexta-feira 13". O que significa na realidade é "à meia-noite do dia 13 de cada mês e também à meia-noite de todas as sextas-feiras": mais de sessenta execuções por ano em vez de uma ou duas.

O comportamento de E que esperava só se aplica quando um dos dois campos é *. Assim, 0 0 * * 5 é todas as sextas-feiras e 0 0 13 * * é todos os dias 13, ambos perfeitamente normais. É restringir os dois que desencadeia o OU.

Não há forma normalizada de exprimir "sexta-feira 13" em cinco campos. O contorno habitual é agendar para todos os dias 13 e fazer com que a tarefa verifique o dia da semana antes de fazer seja o que for.

Uma subtileza relacionada: algumas implementações decidem se um campo está restringido verificando se ele começa literalmente por *. Nessas, 0-6 no campo do dia da semana conta como restringido, mesmo cobrindo todos os dias, ligando discretamente o comportamento de OU. Use * quando quiser dizer "qualquer".

Erros que vale a pena procurar

  • * */2 * * * corre a cada minuto durante uma hora sim, outra não: 720 execuções por dia. O que queria era 0 */2 * * *, que dá 12.
  • Um campo de minuto em falta desloca tudo uma posição para a esquerda e normalmente continua a ser lido sem erro, dando-lhe um horário válido a uma hora completamente errada.
  • 0 0 31 * * salta em silêncio os meses que não têm dia 31.
  • O cron usa o fuso horário do sistema ou do utilizador, a não ser que o agendador lhe permita definir um. Quando os relógios mudam para a hora de verão, uma tarefa colocada na hora saltada pode não correr de todo e uma colocada na hora repetida pode correr duas vezes, consoante a implementação. As tarefas que têm de correr uma vez por dia ficam mais seguras se forem agendadas longe da madrugada em que a mudança acontece.

Na dúvida, leia a expressão em voz alta como uma frase e verifique depois as próximas vezes em que ela dispararia de facto. Se essas datas não forem as que descreveu, a expressão é que está errada, não a sua leitura.