Por que meu crontab não está funcionando e como posso solucioná-lo?

226

Esta é uma pergunta canônica sobre o uso do cron & crontab.

Você foi direcionado aqui porque a comunidade tem quase certeza de que a resposta para sua pergunta pode ser encontrada abaixo. Se sua pergunta não for respondida abaixo, as respostas o ajudarão a reunir informações que ajudarão a comunidade a ajudá-lo. Esta informação deve ser editada na sua pergunta original.

A resposta para ' Por que meu crontab não está funcionando e como posso solucioná-lo? 'pode ser visto abaixo. Isso aborda o cronsistema com o crontab destacado.

Eric Leschinski
fonte
2
Este é um grande embuste de razões pelas quais o crontab não funciona no AskUbuntu.
Dan Dascalescu
1
@DanDascalescu Parece que Eric precisa ter mais representantes
eu sou a pessoa mais estúpida
1
Acabei de ingressar no Server Fault SE (apenas 101 rep), mas gostaria de dar uma questão a -1 !! Essa pergunta foi feita apenas para obter rep? @IamtheMostStupidPerson Concordo totalmente com você ...
Holyprogrammer 04/03
A ideologia ocidental desses padawans de 13 anos é ao mesmo tempo livro e ofuscante, como uma supernova. Para responder às duas perguntas: sim, fiz isso pelo representante e, sim, Eric precisa ter mais reputação. Quanto mais representante eu preciso? Mais. youtu.be/IaDt9T7BF38?t=262
Eric Leschinski

Respostas:

317

Como corrigir todos os problemas / problemas relacionados ao crontab (Linux)


Este é um wiki da comunidade . Se você notar algo incorreto com esta resposta ou tiver informações adicionais, edite-o.


Primeiro, terminologia básica:

  • cron (8) é o daemon que executa comandos agendados.
  • O crontab (1) é o programa usado para modificar os arquivos do usuário crontab (5).
  • O crontab (5) é um arquivo por usuário que contém instruções para o cron (8).

Em seguida, educação sobre o cron:

Todo usuário em um sistema pode ter seu próprio arquivo crontab. A localização dos arquivos raiz e de usuário do crontab depende do sistema, mas geralmente está abaixo /var/spool/cron.

Há um /etc/crontabarquivo em todo o sistema, o /etc/cron.ddiretório pode conter fragmentos de crontab que também são lidos e acionados pelo cron. Algumas distribuições Linux (por exemplo, Red Hat) também possuem /etc/cron.{hourly,daily,weekly,monthly}diretórios, scripts dentro dos quais serão executados a cada hora / dia / semana / mês, com privilégios de root.

o root sempre pode usar o comando crontab; usuários regulares podem ou não receber acesso. Quando você edita o arquivo crontab com o comando crontab -ee o salva, o crond verifica a validade básica, mas não garante que o arquivo crontab esteja formado corretamente. Existe um arquivo chamado cron.denyque especificará quais usuários não podem usar o cron. O cron.denylocal do arquivo depende do sistema e pode ser excluído, o que permitirá que todos os usuários usem o cron.

Se o computador não estiver ligado ou o daemon crond não estiver em execução, e a data / hora para a execução de um comando tiver passado, o crond não recuperará e executará consultas anteriores.

detalhes do crontab, como formular um comando:

Um comando crontab é representado por uma única linha. Você não pode usar \para estender um comando em várias linhas. O #sinal hash ( ) representa um comentário, o que significa que qualquer coisa nessa linha é ignorada pelo cron. Os espaços em branco à esquerda e as linhas em branco são ignorados.

Tenha MUITO cuidado ao usar o %sinal de porcentagem ( ) no seu comando. A menos que sejam escapados, \%eles são convertidos em novas linhas e tudo após o primeiro não escapado %é passado ao seu comando no stdin.

Existem dois formatos para arquivos crontab:

  • Usuário crontabs

    # Example of job definition:
    # .---------------- minute (0 - 59)
    # |  .------------- hour (0 - 23)
    # |  |  .---------- day of month (1 - 31)
    # |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
    # |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7)
    # |  |  |  |  |
    # *  *  *  *  *   command to be executed
    
  • Sistema amplo /etc/crontabe /etc/cron.dfragmentos

    # Example of job definition:
    # .---------------- minute (0 - 59)
    # |  .------------- hour (0 - 23)
    # |  |  .---------- day of month (1 - 31)
    # |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
    # |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7)
    # |  |  |  |  |
    # *  *  *  *  * user-name  command to be executed
    

Observe que o último requer um nome de usuário. O comando será executado como o usuário nomeado.

Os 5 primeiros campos da linha representam o (s) horário (s) em que o comando deve ser executado. Você pode usar números ou nomes aplicáveis ​​de dia / mês, na especificação de horário.

  • Os campos são separados por espaços ou tabulações.
  • Uma vírgula ( ,) é usada para especificar uma lista, por exemplo, 1,4,6,8, o que significa executar em 1,4,6,8.
  • As faixas são especificadas com um traço ( -) e podem ser combinadas com listas, por exemplo, 1-3,9-12, o que significa entre 1 e 3 e depois entre 9 e 12.
  • O /caractere pode ser usado para introduzir uma etapa, por exemplo, 2/5, o que significa começar de 2 a cada 5 (2,7,12,17,22 ...). Eles não passam pelo final.
  • Um asterisco ( *) em um campo significa o intervalo inteiro para esse campo (por exemplo, 0-59para o campo minuto).
  • É possível combinar faixas e etapas, por exemplo, */2significa começar no mínimo para o campo relevante e, a cada 2, por exemplo, 0 por minutos (0,2 ... 58), 1 por meses (1,3 ... 11) etc.

Depurando Comandos Cron

Verifique o email!

Por padrão, o cron enviará qualquer saída do comando para o usuário que estiver executando o comando como. Se não houver saída, não haverá correio. Se você quiser que o cron envie e-mails para uma conta diferente, poderá definir a variável de ambiente MAILTO no arquivo crontab, por exemplo

[email protected]
1 2 * * * /path/to/your/command

Capte a saída você mesmo

Você pode redirecionar stdout e stderr para um arquivo. A sintaxe exata para capturar a saída pode variar dependendo do cron que o shell está usando. Aqui estão dois exemplos que salvam toda a saída em um arquivo em /tmp/mycommand.log:

1 2 * * * /path/to/your/command &>/tmp/mycommand.log
1 2 * * * /path/to/your/command >/tmp/mycommand.log 2>&1

Olhe para os logs

O Cron registra suas ações via syslog, que (dependendo da configuração) costuma ir para /var/log/cronou /var/log/syslog.

Se necessário, você pode filtrar as instruções cron com, por exemplo,

grep CRON /var/log/syslog 

Agora que examinamos o básico do cron, onde estão os arquivos e como usá-los, vamos ver alguns problemas comuns.

Verifique se o cron está em execução

Se o cron não estiver em execução, seus comandos não serão agendados ...

ps -ef | grep cron | grep -v grep

você deve obter algo como

root    1224   1  0 Nov16 ?    00:00:03 cron

ou

root    2018   1  0 Nov14 ?    00:00:06 crond

Se não o reiniciar

/sbin/service cron start

ou

/sbin/service crond start

Pode haver outros métodos; use o que sua distribuição fornece.

O cron executa seu comando em um ambiente restrito.

Quais variáveis ​​de ambiente estão disponíveis provavelmente são muito limitadas. Normalmente, você só vai conseguir algumas variáveis definidas, como $LOGNAME, $HOME, e $PATH.

De nota particular é o PATHé restrito a /bin:/usr/bin. A grande maioria dos problemas "meu script cron não funciona" é causada por esse caminho restritivo . Se o seu comando estiver em um local diferente, você poderá resolver isso de duas maneiras:

  1. Forneça o caminho completo para o seu comando.

    1 2 * * * /path/to/your/command
    
  2. Forneça um PATH adequado no arquivo crontab

    PATH=/usr:/usr/bin:/path/to/something/else
    1 2 * * * command 
    

Se o seu comando exigir outras variáveis ​​de ambiente, você também poderá defini-las no arquivo crontab.

O cron executa seu comando com cwd == $ HOME

Independentemente de onde o programa que você executa reside no sistema de arquivos, o diretório de trabalho atual do programa quando o cron é executado será o diretório inicial do usuário . Se você acessar arquivos no seu programa, precisará levar isso em consideração se usar caminhos relativos, ou (preferencialmente) apenas caminhos totalmente qualificados em todos os lugares e poupar a todos muita confusão.

O último comando no meu crontab não roda

Cron geralmente requer que os comandos sejam finalizados com uma nova linha. Edite seu crontab; vá para o final da linha que contém o último comando e insira uma nova linha (pressione enter).

Verifique o formato crontab

Você não pode usar um usuário crontab formatado em crontab para / etc / crontab ou os fragmentos em /etc/cron.d e vice-versa. Um crontab formatado pelo usuário não inclui um nome de usuário na 6ª posição de uma linha, enquanto um crontab formatado pelo sistema inclui o nome do usuário e executa o comando como esse usuário.

Coloquei um arquivo em /etc/cron.{hourly,daily,weekly,monthly} e ele não é executado

  • Verifique se o nome do arquivo não possui uma extensão, consulte run-parts
  • Verifique se o arquivo possui permissões de execução.
  • Diga ao sistema o que usar ao executar seu script (por exemplo, colocar #!/bin/shno topo)

Erros relacionados à data do Cron

Se sua data foi alterada recentemente por uma atualização de usuário ou de sistema, fuso horário ou outro, o crontab começará a se comportar de maneira irregular e exibirá erros bizarros, às vezes funcionando, às vezes não. Esta é a tentativa do crontab de tentar "fazer o que quiser" quando o tempo mudar de baixo dele. O campo "minuto" ficará ineficaz após a hora ser alterada. Nesse cenário, apenas asteriscos seriam aceitos. Reinicie o cron e tente novamente sem conectar-se à Internet (para que a data não tenha a chance de redefinir para um dos servidores de horário).

Sinais de porcentagem, novamente

Para enfatizar os conselhos sobre sinais de porcentagem, aqui está um exemplo do que o cron faz com eles:

# cron entry
* * * * * cat >$HOME/cron.out%foo%bar%baz

criará o arquivo ~ / cron.out contendo as 3 linhas

foo
bar
baz

Isso é particularmente intrusivo ao usar o datecomando Certifique-se de escapar dos sinais de porcentagem

* * * * * /path/to/command --day "$(date "+\%Y\%m\%d")"
Eric Leschinski
fonte
Também é possível mencionar na seção 'ambiente restrito' que LD_LIBRARY_PATH também pode precisar ter diretórios adicionais configurados, caso sua tarefa cron esteja falhando por não conseguir encontrar bibliotecas compartilhadas.
DavidJ
note que você pode escrever algo assim: 35 1,5-23 / 2 * * * do_something em vez de 35,1,5,7,9, .. * * * Além disso, este crontab.guru traduz as entradas que você faz para linguagem humana.
Dennis Nolte
1
A captura de saída não funciona para mim, pode ser por causa do sh shell. Eu acho que isso é mais portátil: ... /path/to/your/command >/tmp/mycommand.log 2>&1
chus 02/08
Isso funcionou para mim:sudo apt-get install postfix
jmunsch
o trabalho cron também depende de quão pesado é o arquivo? Porque eu corri hello world simples em python com cron, funcionou. Mas meu segundo código era um pouco pesado e normalmente é executado, mas com o cron não está dando saída para o arquivo.
Devendra Bhat
22

O Debian Linux e seus derivados (Ubuntu, Mint, etc) têm algumas peculiaridades que podem impedir a execução de seus trabalhos cron; em particular, os arquivos /etc/cron.d, /etc/cron.{hourly,daily,weekly,monthly}deve:

  • pertencer à raiz
  • só pode ser gravado pela raiz
  • não pode ser gravado por grupo ou outros usuários
  • tem um nome sem pontos '.' ou qualquer outro caractere especial, exceto '-' e '_'.

O último prejudica usuários desavisados ​​regularmente; em particular qualquer script em uma dessas pastas com o nome whatever.sh, mycron.py, testfile.pl, etc, não ser executado, nunca.

Na minha experiência, esse ponto em particular tem sido de longe o motivo mais frequente para um cronjob não executado no Debian e derivados.

Veja man cronpara mais detalhes, se necessário.

wazoox
fonte
19

Se os seus cronjobs pararem de funcionar, verifique se sua senha não expirou.
Haverá mensagens /var/log/messagessemelhantes à abaixo, que mostram problemas com a autenticação do usuário:

(username) FAILED to authorize user with PAM (Authentication token is no longer valid; new one required)

Munkeh72
fonte
2
Acabei de receber isso também (arquivo de mensagem de erro / var / log / syslog para mim). No meu caso, uma caixa DigitalOcean que, no momento da criação, eles redefinem a senha root (opcionalmente) para outra e, aparentemente, até você entrar lá e alterá-la, todos os trabalhos cron não são executados. Vadio. Fix é algo comosudo -u root passwd
rogerdpack
12

Horários incomuns e irregulares

Cron é considerado um agendador muito básico e a sintaxe não permite que um administrador formule agendamentos um pouco mais incomuns.

Considere o seguinte trabalho, que normalmente seria explicado como "executado a commandcada 5 minutos" :

*/5 * * * * /path/to/your/command

versus:

*/7 * * * * /path/to/your/command

que nem sempre é executado a commandcada 7 minutos .

Lembre-se de que o /personagem pode ser usado para introduzir uma etapa, mas essas etapas não ultrapassam o final de uma série, por exemplo, */7que corresponde a cada 7 minutos dos minutos, 0-59 ou seja, 0,7,14,21,28,35,42,49, 56 mas entre uma hora e o próximo haverá apenas 4 minutos entre lotes , depois de 00:56uma nova série começa no 01:00, 01:07etc. (e lotes não será executado em 01:03, 01:10, 01:17etc.).


O que fazer em vez disso?

Crie vários lotes

Em vez de um único trabalho cron, crie vários lotes que combinados resultam na programação desejada.

Por exemplo, para executar um lote a cada 40 minutos (00:00, 00:40, 01:20, 02:00 etc.), crie dois lotes, um que será executado duas vezes nas horas pares e o segundo que será executado apenas nas horas ímpares:

# The following lines create a batch that runs every 40 minutes i.e.

# runs on   0:00, 0:40,        02:00, 02:40         04:00 etc to 22:40
0,40 */2 * * * /path/to/your/command

# runs on               01:20,               03:20,       etc to 23:20
20 1/2 * * * /path/to/your/command

# Combined: 0:00, 0:40, 01:20, 02:00, 02:40, 03:20, 04:00 etc.

Execute seus lotes com menos frequência

Em vez de executar seu lote a cada 7 minutos, que é uma programação difícil de dividir em vários lotes, basta executá-lo a cada 10 minutos.

Inicie seus lotes com mais frequência (mas impeça a execução simultânea de vários lotes)

Muitas programações ímpares evoluem porque os tempos de execução do lote aumentam / flutuam e, em seguida, os lotes são programados com um pouco de margem de segurança adicional para impedir que execuções subsequentes do mesmo lote se sobreponham e sejam executadas simultaneamente.

Em vez disso, pense de maneira diferente e crie um cronjob que falhará normalmente quando uma execução anterior ainda não tiver sido concluída, mas que será executada de outra forma. Veja estas perguntas e respostas :

* * * * * /usr/bin/flock -n /tmp/fcj.lockfile /usr/local/bin/frequent_cron_job 

Isso iniciará quase imediatamente uma nova execução assim que a execução anterior de / usr / local / bin / frequent_cron_job for concluída.

Inicie seus lotes com mais frequência (mas saia normalmente quando as condições não forem adequadas)

Como a sintaxe do cron é limitada, você pode decidir colocar condições e lógicas mais complexas no trabalho em lotes em si (ou em um script de wrapper em torno do trabalho em lotes existente). Isso permite que você utilize os recursos avançados de suas linguagens de script favoritas, para comentar seu código e evitará construções de leitura difícil na própria entrada do crontab.

No bash, seven-minute-jobseria então algo como:

#!/bin/bash
# seven-minute-job
# This batch will only run when 420 seconds (7 min) have passed
# since the file /tmp/lastrun was either created or updated

if [ ! -f /tmp/lastrun ] ; then
    touch /tmp/lastrun
fi

if [ $(( $(date +%s) - $(date -r /tmp/lastrun +%s) )) -lt 420 ] ; then
     # The minimum interval of 7 minutes between successive batches hasn't passed yet.
    exit 0
fi

####  Start running your actual batch job below

/path/to/your/command

#### actual batch job is done, now update the time stamp
date > /tmp/lastrun
#EOF

Que você pode executar com segurança (tentativa) a cada minuto:

* * * * * /path/to/your/seven-minute-job

Um problema diferente, mas semelhante seria para agendar um lote para ser executado na primeira segunda-feira de cada mês (ou a segunda quarta-feira) etc Basta agendar o lote para executar toda segunda-feira e sair quando a data não é nem entre o 1 st ou 7 th e o dia da semana não é segunda-feira.

#!/bin/bash
# first-monday-of-the-month-housekeeping-job

# exit if today is not a Monday (and prevent locale issues by using the day number) 
if [ $(date +%u) != 1 ] ; then
  exit 0
fi

# exit if today is not the first Monday
if [ $(date +%d) -gt 7 ] ; then
  exit 0
fi

####  Start running your actual batch job below

/path/to/your/command

#EOF

Que você pode executar com segurança (tentativa) para executar toda segunda-feira:

0 0 * * 1 /path/to/your/first-monday-of-the-month-housekeeping-job

Não use cron

Se suas necessidades forem complexas, considere o uso de um produto mais avançado, projetado para executar agendas complexas (distribuídas em vários servidores) e que suporte gatilhos, dependências de tarefas, manipulação de erros, tentativas e monitoramento de novas tentativas, etc. O jargão da indústria seria "corporativo" " agendamento de tarefas e / ou" automação de carga de trabalho ".

HBruijn
fonte
8

Específico para PHP

Se você tem algum trabalho cron como:

php /bla/bla/something.php >> /var/logs/somelog-for-stdout.log

E em caso de erros esperados, eles serão enviados a você, mas não - verifique isso.

Por padrão, o PHP não envia erros para STDOUT. @see https://bugs.php.net/bug.php?id=22839

Para consertar isso, adicione no php.ini do cli ou na sua linha (ou no wrapper do bash para PHP):

  • --define display_startup_errors = 1
  • --define display_errors = 'stderr'

A primeira configuração permitirá que você tenha fatais como 'Memory oops' e a segunda - para redirecionar todos eles para o STDERR. Somente depois que você puder dormir bem, todos serão enviados para o e-mail do root em vez de apenas serem registrados.

gaRex
fonte
2
Esse relatório de erro foi fechado em 2007 com o status do patch sendo adicionado às ramificações do PHP 5.2+. Tem certeza de que isso é necessário? Eu apenas tentei no PHP 5.4 e parece funcionar bem. (Ainda é necessário para o PHP 4).
Xeoncross
@Xeoncross ver data da resposta :)
gaRex
1
Sim, foi isso que me confundiu desde que você respondeu em 2013 e o ingresso estava de volta em '07.
Xeoncross
0

Adicionando minha resposta a partir daqui para garantir a integridade e adicionando outro recurso potencialmente útil:

O cronusuário tem um diferente $PATHde você:

Um problema frequente que os usuários cometem com as crontabentradas é que elas esquecem que a cronexecução é diferente da environmentde um usuário conectado. Por exemplo, um usuário cria um programa ou script em seu $HOMEdiretório e insere o seguinte comando para executá-lo:

$ ./certbot ... 

O comando é executado perfeitamente em sua linha de comando. O usuário então adiciona esse comando ao dele crontab, mas descobre que isso não funciona:

*/10 * * * * ./certbot ....

O motivo da falha nesse caso é que ./é um local diferente para o cronusuário e para o usuário conectado. Ou seja, o environmenté diferente! O PATH faz parte do environmente geralmente é diferente para o cronusuário. Para complicar este assunto é que o environmentpara cronnão é o mesmo para todos os * nix distribuições, e há várias versões decron

Uma solução simples para esse problema específico é fornecer ao cronusuário uma especificação completa do caminho na crontabentrada:

0 22 * * * /path/to/certbot .....

Qual é o cronusuário environment?

Em alguns casos, talvez seja necessário conhecer a environmentespecificação completa de cronnosso sistema (ou podemos apenas estar curiosos). O que é environmentpara o cronusuário e como ele é diferente do nosso? Além disso, podemos precisar saber environmentpara outro cronusuário - rootpor exemplo ... o que o rootusuário está environmentusando cron? Uma maneira de aprender isso é pedir cronpara nos dizer:

  1. Crie um script de shell no diretório inicial ( ~/) da seguinte forma (ou com o editor de sua escolha):
$ nano ~/envtst.sh
  1. Digite o seguinte no editor, depois de ajustar para o seu sistema / usuário:
#!/bin/sh 
/bin/echo "env report follows for user "$USER >> /home/you/envtst.sh.out 
/usr/bin/env >> /home/you/envtst.sh.out 
/bin/echo "env report for user "$USER" concluded" >> /home/you/envtst.sh.out
/bin/echo " " >> /home/you/envtst.sh.out
  1. Salve o arquivo, saia do editor e defina as permissões de arquivo como executável.
$ chmod a+rx ~/envtst.sh
  1. Execute o script que você acabou de criar e revise a saída /home/you/envtst.sh.out. Esta saída mostrará seu ambiente atual como o que $USERvocê está conectado como:
$ ./envtst.sh $$ cat /home/you/envtst.sh.out
  1. Abra o seu crontabpara edição:
$ crontab -e -u root
  1. Digite a seguinte linha na parte inferior do seu crontab:
* * * * *  /home/you/envtst.sh >> /home/you/envtst.sh.err 2>&1

RESPOSTA: O arquivo de saída /home/you/envtst.sh.outconterá uma lista do environment"usuário cron cron". Depois que você souber disso, ajuste sua crontabentrada de acordo.

Não consigo especificar a programação necessária na minha crontabentrada:

A entrada de agendamento para crontabé obviamente definida em man crontab, e você deve ler isso. No entanto, ler man crontabe entender o cronograma são duas coisas diferentes. E tentativa e erro em uma especificação de cronograma pode se tornar muito entediante. Felizmente, existe um recurso que pode ajudar: o gont crontab. . Digite sua especificação de programação e ela explicará a programação em idioma inglês simples.

Finalmente, e correndo o risco de ser redundante com uma das outras respostas aqui, não fique preso ao pensar que você está limitado a uma única crontabentrada, porque você tem um trabalho a agendar. Você pode usar quantas crontabentradas precisar para obter o cronograma necessário.

Seamus
fonte