No Debian 8.1, estou usando um recurso Bash para detectar se o site stackoverflow.com está acessível:
(echo >/dev/tcp/stackoverflow.com/80) &>/dev/null || echo "stackoverflow unreachable"
Isso é específico do Bash e não funcionará em sh
, o shell padrão de cron
.
Se, de propósito, tentarmos o script em sh
, obteremos:
$ /bin/sh: 1: cannot create /dev/tcp/stackoverflow.com/80: Directory nonexistent
Portanto, se eu apenas colocar o seguinte no meu crontab pessoal (sem definir SHELL
to /bin/bash
) via crontab -e
, espero que uma vez por minuto, o script seja executado e, portanto, espero também obter o erro acima enviado por email uma vez por minuto:
* * * * * (echo >/dev/tcp/stackoverflow.com/80) &>/dev/null || echo "stackoverflow unreachable"
E, de fato, exatamente como esperado, vemos a partir de /var/log/syslog
que a entrada é executada uma vez por minuto:
# sudo grep stackoverflow /var/log/syslog
Aug 24 18:58:01 localhost CRON[13719]: (mat) CMD ((echo >/dev/tcp/stackoverflow.com/80) &>/dev/null || echo "stackoverflow unreachable")
Aug 24 18:59:01 localhost CRON[13723]: (mat) CMD ((echo >/dev/tcp/stackoverflow.com/80) &>/dev/null || echo "stackoverflow unreachable")
Aug 24 19:00:01 localhost CRON[13727]: (mat) CMD ((echo >/dev/tcp/stackoverflow.com/80) &>/dev/null || echo "stackoverflow unreachable")
...
Durante as últimas ~ 2 horas, isso foi executado mais de 120 vezes, já que posso verificar com o piping da saída para wc -l
.
No entanto , destes > 120 vezes o comando shell (para repetir: o comando shell é inválido para /bin/sh
) foi executado, eu só tenho três e-mails:
O primeiro às 19:10:01, o segundo às 20:15:01 e o terceiro às 20:57:01.
O conteúdo de todos os três e-mails é lido exatamente como esperado e contém exatamente a mensagem de erro esperada da execução do script em um shell incompatível (propositalmente). Por exemplo, a segunda mensagem que recebi lê (e as outras duas são virtualmente idênticas):
From [email protected] Mon Aug 24 20:15:01 2015
From: [email protected] (Cron Daemon)
To: [email protected]
Subject: Cron (echo >/dev/tcp/stackoverflow.com/80)&>/dev/null || echo "stackoverflow unreachable"
...
/bin/sh: 1: cannot create /dev/tcp/stackoverflow.com/80: Directory nonexistent'
De /var/log/mail.log
, vejo que esses três e-mails foram os únicos e-mails enviados e recebidos nas últimas horas.
Assim, onde estão os > 100 e-mails adicionais que esperamos receber do cron devido à saída acima criada pelo script incorreto?
Para resumir:
- O correio está configurado corretamente neste sistema, posso enviar e receber e-mails sem problemas com
/usr/bin/sendmail
.
- Cron é configurado corretamente, percebe a tarefa como esperado e a executa precisamente nos horários configurados. Eu tentei muitas outras tarefas e opções de agendamento, e o cron executou todas exatamente como esperado.
- O script sempre grava a saída (veja abaixo) e esperamos que o cron envie a saída para mim por correio para cada chamada.
- A saída é enviada para mim apenas ocasionalmente e aparentemente ignorada na maioria dos casos.
Existem muitas maneiras de contornar o erro óbvio que levou às observações acima:
- posso definir
SHELL=/bin/bash
no meu crontab
.
- Eu posso criar um
heartbeat.sh
com #!/bin/bash
e invocar isso.
- Eu posso invocar o script com
/bin/bash -c ...
em crontab
.
- etc, todos consertando o erro de usar um recurso específico do Bash dentro de
sh
.
No entanto, tudo isso não aborda o problema principal desta questão, que é que, neste caso, cron
não envia emails de maneira confiável, embora o script sempre crie saída.
Eu verifiquei que o script sempre cria saída criando wrong.sh
(que novamente de propósito usa o shell /bin/sh
inadequado, para produzir o mesmo erro que cron
deve ver):
#!/bin/sh
(echo >/dev/tcp/stackoverflow.com/80) &>/dev/null || echo "stackoverflow unreachable"
Agora posso invocar o script em um loop e ver se existe um caso em que ele termina sem criar saída. Usando Bash :
$ while true; do [[ -n $(./wrong.sh 2>&1 ) ]]; echo $?; done | grep -v 0
Mesmo em milhares de invocações, não consegui reproduzir um caso em que o script é concluído sem criar saída.
Qual pode ser a causa desse comportamento imprevisível? Alguém pode reproduzir isso? Para mim, parece que pode haver uma condição de corrida em que o cron pode perder a saída de um script, possivelmente envolvendo principalmente casos em que o erro deriva do próprio shell. Obrigada!