>_ josetilabs.com
← blog

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

cron
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

bash
#!/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.

Terminal com dois painéis: crontab ingênuo sem PATH nem log, e crontab corrigido com SHELL, PATH, MAILTO vazio, redirecionamento para log e flock no wrapper
A diferença está nas três primeiras linhas e no redirecionamento da última.

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.

Leia também

// Por e-mail

Os textos publicados aqui também vão por e-mail. O envio ainda não começou: quem deixar o endereço agora entra na primeira lista.

Guardo só o seu e-mail, a data e o registro desta autorização. Sem cookies, sem rastreador e nada compartilhado com terceiros. Detalhes na política de privacidade.