Explicador de expressões cron

Explica qualquer expressão cron — Vixie, Quartz, EventBridge ou Jenkins —, lista as próximas execuções em qualquer fuso e assinala as armadilhas.

Entrada

Lida como Vixie cron.

O que significa

Às 00:00 no dia 13 do mês ou à sexta-feira

Campo a campo

CampoEscritoCorresponde a
Minuto00
Hora00
Dia do mês1313
Mês*qualquer valor
Dia da semana5sexta-feira

A que prestar atenção

  • Os dois campos de dia estão restringidos, por isso o cron executa isto quando qualquer um deles corresponder: o dia do mês ou o dia da semana, não os dois juntos. É por isso que 13 com sexta-feira significa o dia 13 e todas as sextas-feiras, e não sexta-feira 13.

Próximas execuções

    O que esta ferramenta faz

    Lê-lhe de volta uma expressão cron por palavras, enumera as próximas vezes em que ela vai disparar de facto no fuso horário em que a tarefa corre, e nomeia aquilo que a expressão implica mas não diz. Esta última parte é o essencial: uma expressão cron nunca está errada de uma forma que produza um erro. Corre nos dias errados, em silêncio, até alguém reparar.

    São compreendidos quatro dialetos e não apenas um. A forma de cinco campos que o crontab aceita, o Quartz com o seu campo de segundos e as suas regras de calendário, o AWS EventBridge com um ano no fim, e o Jenkins com o seu hash. Colar um deles numa ferramenta que só conhece outro é como a maioria das pessoas descobre que existe mais do que um.

    Os campos, e quantos são

    A expressão clássica tem cinco campos separados por espaços, numa ordem fixa: minuto, hora, dia do mês, mês, dia da semana. Cada um é um asterisco que significa «todos», um número, uma lista separada por vírgulas, um intervalo com hífen ou um passo com barra. Os meses e os dias podem escrever-se com nomes de três letras, quase sempre mais claros do que os algarismos.

    • Um asterisco significa qualquer valor que o campo possa tomar.
    • Um intervalo como 9-17 significa todos os valores de 9 a 17, inclusive.
    • Um passo como */15 significa cada 15.º valor a partir do fundo do campo; 9-17/2 avança dentro de um intervalo e 5/15 avança de 5 até ao topo.
    • Uma lista como 0,30 significa exatamente esses valores.
    • Um intervalo degenerado como 13-13 é simplesmente 13 — algumas bibliotecas erram aqui e tratam-no como um asterisco.

    Um sexto campo é onde os dialetos se separam. O Quartz põe-no à frente e lê-o como segundos; o EventBridge põe-no no fim e lê-o como ano. Seis campos nus são por isso genuinamente ambíguos, e esta ferramenta diz como os leu em vez de escolher em silêncio. Envolver a expressão em cron(...) — a sintaxe própria do EventBridge — resolve a questão.

    As macros são atalhos: @hourly, @daily, @midnight, @weekly, @monthly, @yearly e @annually. Expandem-se em expressões correntes, que a ferramenta mostra. @reboot é a exceção e não é um agendamento de todo.

    Os dois campos de dia, e o «ou» que parece um «e»

    Uma data pode escolher-se de duas maneiras diferentes — pelo dia do mês e pelo dia da semana — e o cron tem os dois campos. Quando ambos estão restringidos, a tarefa corre quando qualquer um deles corresponder. Não os dois.

    Portanto 0 0 13 * 5 não é sexta-feira 13. É o dia 13 de cada mês, e todas as sextas-feiras: cerca de 64 dias por ano em vez de um ou dois. A expressão parece uma conjunção e comporta-se como uma disjunção, nada reporta um erro, e a tarefa corre simplesmente mais vezes do que se pretendia.

    A regra tem uma segunda metade, mais estranha, que quase ninguém conhece. O cron não verifica se um campo de dia está restringido; verifica se o primeiro carácter do campo é um asterisco. Um passo como */14 conta assim como asterisco embora restrinja o campo ao dia 1, ao 15 e ao 29 — e a sua presença faz os dois campos de dia voltar do «ou» ao «e». Duas expressões que parecem igualmente restringidas comportam-se então de forma completamente diferente, e esta ferramenta indica qual a regra em vigor.

    O Quartz e o EventBridge contornam toda a questão recusando que ambos os campos digam alguma coisa: exatamente um deles tem de ser um ponto de interrogação, que significa «nenhum valor concreto, decide o outro campo».

    Passos que não dividem o seu campo

    Um passo escreve-se como se fosse um intervalo, e dentro de um ciclo do campo é-o. Ao atravessar o limite, normalmente não.

    */7 no campo das horas significa as horas 0, 7, 14 e 21. Depois das 21 o campo acaba, por isso a execução seguinte é às 0 do dia seguinte — três horas depois, não sete. Cada ciclo termina com um intervalo curto, e o mesmo acontece com */7 nos minutos (um buraco de quatro minutos depois do 56) e com qualquer outro passo que não divida o seu intervalo de forma exata. */15 nos minutos e */6 nas horas são seguros; a maioria dos números a que se recorre não é.

    Isto importa quando o passo é escolhido para distribuir trabalho. Uma tarefa em */7 horas não corre de sete em sete horas, e um limite de taxa dimensionado para sete será ultrapassado uma vez por dia.

    Os dias da semana são numerados de forma diferente em sítios diferentes

    O Vixie cron numera os dias de 0 a 7 com o domingo em ambas as pontas, pelo que 0 e 7 são o mesmo dia e 1 é segunda-feira. O Quartz e o EventBridge numeram-nos de 1 a 7 com o domingo no 1, por isso aí 1 é domingo e 2 é segunda-feira.

    Uma expressão copiada de uma configuração Quartz para um crontab corre portanto um dia mais cedo, e nada se queixa em lado nenhum, porque ambas as escritas são números válidos nos dois sistemas. Os nomes de três letras — MON, FRI — significam o mesmo dia em toda a parte e são a defesa simples.

    A mudança da hora: as duas manhãs do ano

    Um daemon cron não agenda instantes. Olha para o relógio local a cada minuto e pergunta se a expressão corresponde. Duas vezes por ano esse relógio não é uma sequência bem comportada.

    Quando salta para a frente, uma hora de leituras nunca acontece. Uma tarefa marcada para as 02:30 não tem 02:30 nesse dia. Quando recua, uma hora de leituras acontece duas vezes, e uma tarefa marcada para a 01:30 tem duas.

    O Vixie cron distingue aqui dois tipos de tarefa, e a distinção vale a pena porque decide quais das suas tarefas são afetadas. Uma tarefa presa a uma hora de relógio — hora e minuto fixos — é executada exatamente uma vez nos dois casos: depois de um salto para a frente arranca de imediato, e depois de um recuo o cron tem o cuidado de não a repetir. Uma tarefa com um asterisco na hora ou no minuto é uma tarefa de intervalo e limita-se a seguir o relógio: perde uma hora de execuções na primavera e repete outra no outono.

    Assim, uma cópia de segurança horária corre mesmo duas vezes numa noite por ano, e uma tarefa noturna às 02:30 corre mesmo num momento invulgar noutra noite. Esta ferramenta confronta os próximos doze meses do fuso escolhido com a sua expressão e diz qual dos dois casos se aplica, com a data.

    A saída fiável é agendar a tarefa em UTC, ou fora do intervalo local das 01:00 às 03:00, onde não cai nenhuma transição.

    Em que relógio a tarefa está realmente

    Um crontab corre no fuso local da máquina, ou naquele que CRON_TZ ou TZ indicarem no topo do ficheiro. Raramente é o fuso de quem lê a expressão, e é por isso que o fuso horário aqui é uma definição e não o do seu navegador.

    Um CronJob do Kubernetes é outra coisa: lê o seu agendamento em UTC a não ser que o manifesto defina um campo timeZone, diga o que disser o relógio do nó. Um agendamento escrito para o horário de expediente local e implantado sem esse campo corre à hora errada em toda a parte fora de UTC — e não muda com as estações, o que é um bug ou um alívio consoante a tarefa.

    O Jenkins e o H

    O Jenkins acrescenta um símbolo que nenhum outro dialeto tem: H, que parece aleatoriedade e não é. O Jenkins faz o hash do nome da tarefa num valor fixo dentro do intervalo do campo, por isso a tarefa corre à mesma hora todos os dias — a hora dela — enquanto cem tarefas escritas H * * * * se espalham uniformemente pela hora em vez de arrancarem todas ao minuto zero.

    Como o valor vem do nome da tarefa, e o nome não faz parte da expressão, nenhuma ferramenta pode calcular as horas exatas. Esta resolve cada H no fundo do seu intervalo e di-lo: a forma do agendamento — de quanto em quanto tempo, em que dias — é exata, e só o desvio dentro de cada período é um substituto.

    O Jenkins também redefine as macros. O seu @daily é @midnight, que não é meia-noite mas um minuto com hash algures nas três primeiras horas do dia.

    O que o cron não faz

    • Não recupera o que falhou. Se a máquina estava adormecida ou o daemon em baixo quando uma execução era devida, essa execução não acontece mais tarde: é simplesmente perdida. O anacron existe exatamente para isto e é outro programa.
    • Não impede execuções sobrepostas. Se uma tarefa demora mais do que o intervalo, a seguinte arranca à mesma, e ao fim de um tempo há várias. Um ficheiro de bloqueio é a resposta habitual.
    • Na forma clássica não tem noção de segundos. A resolução mais fina é um minuto; qualquer coisa mais rápida precisa de Quartz, systemd ou um ciclo dentro da própria tarefa.
    • @reboot não é um agendamento. Corre uma vez ao arrancar o daemon, por isso não tem próxima execução, e numa máquina que nunca reinicia nunca corre.
    • Uma expressão rate no EventBridge conta a partir da criação da regra, e esse momento não está na expressão: nenhuma ferramenta pode dizer quando dispara a seguir.

    Perguntas frequentes

    Como executo uma tarefa na primeira segunda-feira do mês?
    Não com uma expressão cron simples: isso exige um «e» entre os dois campos de dia, e o cron dá um «ou». O contorno habitual é agendar 0 0 1-7 * * e deixar o próprio comando testar o dia da semana. O Quartz e o EventBridge conseguem exprimi-lo diretamente, como 0 0 12 ? * MON#1.
    Porque é que 0 0 13 * 5 corre tantas vezes?
    Porque significa o dia 13 ou todas as sextas-feiras, e não sexta-feira 13. Quando os dois campos de dia estão restringidos e nenhum começa por asterisco, o cron executa a tarefa quando qualquer um corresponder. No dialeto clássico não há maneira de escrever um «e».
    Qual é a diferença entre */5 e 0-59/5?
    Nenhuma, para o campo dos minutos: ambos dão 0, 5, 10 e assim por diante. A diferença aparece nos dois campos de dia, onde o cron decide entre «e» e «ou» olhando se o campo começa por asterisco — logo */5 e 0-59/5 selecionam os mesmos dias mas combinam-se de forma diferente com o outro campo de dia.
    A minha tarefa corre duas vezes quando o relógio recua?
    Se tiver um asterisco na hora ou no minuto, sim: segue o relógio, e essa hora acontece duas vezes. Se tiver hora e minuto fixos, não — o Vixie cron executa uma tarefa dessas uma só vez. O painel de observações diz qual é o seu caso, com a data em que se aplica.
    Que fuso horário usa uma expressão cron?
    O da máquina, a não ser que CRON_TZ ou TZ estejam definidos no crontab. Um CronJob do Kubernetes usa UTC a não ser que o manifesto defina timeZone. Como o fuso não faz parte da expressão, esta ferramenta pergunta-o em vez de assumir o do seu navegador.
    Posso agendar algo de 90 em 90 minutos?
    Não como uma única expressão, porque cada campo cicla por si e 90 minutos não cabem numa hora. A escrita habitual são duas expressões, 0 0,3,6,9,12,15,18,21 * * * e 30 1,4,7,10,13,16,19,22 * * *, que juntas dão uma execução a cada 90 minutos.
    Alguma coisa do que eu colo é enviada para um servidor?
    Não. A análise, a explicação e todo o cálculo do agendamento correm no seu navegador; nada é carregado nem registado, e funciona sem ligação à rede.