Voltar para blog

Corrigindo o erro $'\r': command not found em scripts Bash com diagnóstico forense e prevenção no pipeline

07/03/2026 · 3 min · Linux

Compartilhar

Quando um script aparentemente correto falha no Linux com mensagens como \$'\\r': command not found, syntax error near unexpected token ou \$'{\\r'', o problema quase sempre está na camada de encoding e terminação de linha, não na lógica Bash.

Neste artigo, documentei o procedimento que uso em produção para:

  1. comprovar tecnicamente a origem da falha;
  2. corrigir o arquivo sem efeitos colaterais;
  3. blindar o pipeline para que o incidente não volte.

1) Causa raiz: CRLF (Windows) em runtime Linux

A diferença estrutural é simples, mas crítica:

Quando o Bash encontra \r no fim da linha, ele interpreta esse byte extra como parte do token/comando. O resultado é erro de comando inexistente (\r) ou erro sintático em pontos que visualmente parecem corretos.

Exemplo clássico observado em campo:

./deploy.sh: line 2: $'\r': command not found
./deploy.sh: line 18: syntax error near unexpected token `$'{\r''

2) Diagnóstico técnico confiável (sem suposição)

Antes de “consertar”, valide evidência. Eu uso quatro verificações.

2.1 file: detecção objetiva de terminador de linha

file script.sh

Se a saída contiver with CRLF line terminators, o diagnóstico está fechado.

2.2 cat -v: visualização de caracteres não imprimíveis

cat -v script.sh | sed -n '1,40p'

Se aparecer ^M no fim das linhas, esse ^M é o \r que está quebrando a execução.

2.3 od/hexdump: prova binária

od -An -tx1 -N 80 script.sh
# ou
hexdump -C script.sh | head

Procure sequência 0d 0a (CRLF). Em Linux puro, o esperado é apenas 0a.

2.4 Validação de shebang e permissões (não confundir causa)

head -n 1 script.sh
ls -l script.sh

O script deve começar com shebang válido (#!/usr/bin/env bash ou #!/bin/bash) e permissão de execução adequada. Isso não resolve CRLF, mas elimina falso positivo de diagnóstico.

3) Correção segura: três métodos operacionais

3.1 Método preferido: dos2unix

Ferramenta dedicada e previsível para conversão CRLF -> LF.

dos2unix script.sh

Instalação:

# Debian/Ubuntu
sudo apt update && sudo apt install -y dos2unix

# RHEL/CentOS/Alma/Rocky
sudo dnf install -y dos2unix

3.2 Método universal sem pacote extra: sed

sed -i 's/\r$//' script.sh

Esse comando remove apenas \r no final de cada linha, preservando o restante do conteúdo.

3.3 Pipeline com saída temporária: tr

tr -d '\r' < script.sh > script.sh.lf && mv script.sh.lf script.sh

Uso quando quero comparação explícita entre arquivo original e convertido antes do replace final.

4) Correção em lote de diretórios inteiros (com proteção)

Em incidentes reais, o problema raramente vem em um único arquivo. Para tratar em lote sem afetar binários:

find . -type f \( -name "*.sh" -o -name "*.bash" -o -name "*.env" \) -print0 \
  | xargs -0 dos2unix

Validação pós-conversão:

find . -type f -name "*.sh" -exec file {} \; | grep -i crlf && echo "ainda existe CRLF" || echo "todos os scripts em LF"

5) Caso recorrente em produção: CRLF + bad interpreter

Além de \$'\\r': command not found, também encontrei:

/bin/bash^M: bad interpreter: No such file or directory

Isso acontece quando o \r contamina até a linha do shebang. A correção é idêntica (LF), seguida de novo teste de execução.

6) Hardening definitivo no fluxo DevOps

Remediar arquivo resolve incidente atual. Para evitar reincidência, padronizei repositório, editor e Git.

6.1 .editorconfig para governança de EOL

root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2

[*.sh]
indent_size = 2

6.2 .gitattributes para normalização no repositório

* text=auto
*.sh text eol=lf
*.bash text eol=lf
*.env text eol=lf

6.3 Configuração Git recomendada no workstation

git config --global core.autocrlf input
git config --global core.eol lf

Com input, o Git converte CRLF -> LF no commit e evita que scripts subam contaminados.

6.4 Gate de qualidade no CI

Incluí validação automática para quebrar pipeline ao detectar CRLF em scripts:

#!/usr/bin/env bash
set -euo pipefail

if git ls-files '*.sh' '*.bash' | xargs file | grep -qi 'CRLF'; then
  echo "ERRO: scripts com CRLF detectados"
  git ls-files '*.sh' '*.bash' | xargs file | grep -i 'CRLF'
  exit 1
fi

echo "OK: todos os scripts em LF"

7) Runbook rápido de resposta a incidente

Quando o erro aparece em produção, executo essa sequência:

# 1) confirmar problema
file deploy.sh
cat -v deploy.sh | head

# 2) corrigir
dos2unix deploy.sh
chmod +x deploy.sh

# 3) validar sintaxe
bash -n deploy.sh

# 4) executar com trace controlado
bash -x deploy.sh

Se for lote, aplico nos scripts do release e repito bash -n em todos:

find scripts -type f -name '*.sh' -exec bash -n {} \;

8) Conclusão técnica

O erro \$'\\r': command not found não é bug de Bash, é dívida de padronização entre Windows e Linux. Em operação profissional, esse tipo de falha deve ser tratado como problema de supply chain de código (editor + VCS + CI), não como incidente isolado de script.

Com diagnóstico por evidência (file, cat -v, hexdump), correção controlada (dos2unix/sed) e hardening de pipeline (.editorconfig, .gitattributes, checks de CI), o problema deixa de existir no dia a dia.

CC BY-NC

Este post está licenciado sob CC BY-NC.

Comentários

Participe da discussão abaixo.