21 out 2026 · linux, automacao · 4 min
Cron do jeito certo: ambiente, log e o que não roda
O cron roda com PATH=/usr/bin:/bin, SHELL=/bin/sh, a partir do $HOME do usuário e sem carregar .bashrc. Por isso um script que funciona no terminal falha em silêncio no cron. A correção cabe em três linhas no crontab: declare PATH e SHELL, desligue o email com MAILTO=”“, e redirecione a saída para um log. O wrapper abaixo acrescenta trava por arquivo, para uma execução não começar em cima da outra.
O ambiente que o cron não herda
Cron não é um shell. Ele não lê .bashrc, .profile nem /etc/profile. As variáveis que ele define são poucas: SHELL=/bin/sh, HOME e LOGNAME do /etc/passwd, e PATH=/usr/bin:/bin no Debian e no Ubuntu (no root, /usr/sbin:/usr/bin). O diretório de trabalho é o $HOME. Foi isso que reproduzi: com PATH=/usr/bin:/bin, date e flock existem, mas qualquer binário em /usr/local/bin ou em ~/.local/bin não existe para o cron. Um cd relativo também falha, porque o diretório de partida não é o do script.
Medição: com PATH=/usr/bin:/bin, date e flock foram encontrados, e o cd para um diretório inexistente falhou. O PATH mínimo é o motivo de metade dos “funciona no terminal, não no cron”.
O crontab
SHELL=/bin/sh
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=""
0 * * * * /opt/meu-job.sh >> /tmp/meu-job.log 2>&1
MAILTO=”” desliga o email. Sem ele, qualquer saída do comando vira email para o dono do crontab; sem MTA instalado, a saída some no syslog e você nunca vê. O redirecionamento >> ... 2>&1 grava stdout e stderr no mesmo arquivo, em modo append, para não perder histórico.
O wrapper com trava
#!/usr/bin/env bash
set -euo pipefail
LOG="${LOG:-/tmp/meu-job.log}"
LOCK="${LOCK:-/tmp/meu-job.lock}"
mkdir -p "$(dirname "$LOG")" "$(dirname "$LOCK")"
exec 9>"$LOCK"
if ! flock -n 9; then
printf '%s outra instancia rodando, saindo
' "$(date -Is)" >> "$LOG"
exit 0
fi
{
printf '===== %s inicio =====
' "$(date -Is)"
# trabalho aqui
printf 'ok
'
printf '===== %s fim =====
' "$(date -Is)"
} >> "$LOG" 2>&1
printf 'meu-job: ok, log em %s
' "$LOG"
flock -n tenta o bloqueio sem esperar. Se outra instância estiver rodando, sai com 0 e registra no log. O descritor 9 fica aberto até o script terminar, então o bloqueio se libera sozinho. Não use pidfile com echo $$: ele mente quando o processo morre sem limpar.
Como verificar
Instale o crontab com crontab arquivo e confira com crontab -l. Rode o wrapper à mão: bash /opt/meu-job.sh deve imprimir meu-job: ok, log em .... Veja o log com tail -f /tmp/meu-job.log. Para confirmar que o cron disparou, procure no syslog: grep CRON /var/log/syslog (Debian/Ubuntu) ou journalctl -u cron.
Se der errado
Comando não encontrado. Causa: PATH mínimo. Correção: declare PATH=/usr/local/bin:/usr/bin:/bin no topo do crontab, ou use caminho absoluto no comando.
Arquivo não encontrado. Causa: diretório de trabalho é $HOME, e o script usou caminho relativo. Correção: caminho absoluto em tudo, ou cd /opt && ./meu-job.sh como comando do cron.
Saída que nunca aparece. Causa: MAILTO padrão tenta enviar email; sem MTA, a saída vai para o syslog e se perde. Correção: MAILTO=”” e redirecionamento >> log 2>&1.
Duas instâncias ao mesmo tempo. Causa: o job demora mais que o intervalo. Correção: flock -n no wrapper, saindo com 0 quando não conseguir o bloqueio.
Tabela
| Sintoma | Causa provável | Correção |
|---|---|---|
| “command not found” no log | PATH mínimo | PATH=… no crontab |
| “No such file” | cwd é $HOME | caminho absoluto |
| email de erro ou silêncio | MAILTO padrão | MAILTO=”” + redirecionamento |
| job duplicado | intervalo menor que duração | flock -n |
FAQ
O cron carrega .bashrc? Não. Ele não lê nenhum arquivo de perfil. Se precisar de variável de ambiente, declare no crontab ou no wrapper.
Posso usar % no comando? O % vira quebra de linha no cron. Escape com \%.
Como rodar a cada 5 minutos? */5 * * * *. Para horário fixo, 0 3 * * * roda às 3h.
Cron ou systemd timer? Para máquina que desliga, systemd timer com Persistent=true recupera execução perdida. Para servidor que fica ligado, cron basta.
Como desligar um job sem apagar? Comente a linha com #. O crontab aceita comentário.
Fontes
crontab(5) e cron(8), conferidos em outubro de 2026. O guia de variáveis de ambiente do cron resume os padrões por distribuição. Para rotina de backup, backup com rsync restaurável. Para não se trancar fora ao automatizar, fail2ban sem se trancar fora.