If a Bash script looks correct but fails with \$'\\r': command not found, bad interpreter, or random token errors, the root cause is usually line-ending encoding mismatch, not script logic.
This is the exact field workflow I use to isolate, fix, and permanently prevent this class of incidents.
1) Root cause: CRLF payload executed in Linux runtime
- Windows line ending:
CRLF(\r\n) - Linux/Unix line ending:
LF(\n)
When \r is present, Bash parses it as part of command tokens.
Typical errors:
./deploy.sh: line 2: $'\r': command not found
./deploy.sh: /bin/bash^M: bad interpreter: No such file or directory
2) Reliable diagnostics (evidence first)
2.1 Check line terminators with file
file script.sh
If output includes CRLF line terminators, that is your direct cause.
2.2 Reveal hidden chars with cat -v
cat -v script.sh | sed -n '1,40p'
^M at line endings means \r contamination.
2.3 Binary-level proof with od/hexdump
od -An -tx1 -N 80 script.sh
# or
hexdump -C script.sh | head
Look for 0d 0a (CRLF). Linux-native files should show 0a only.
3) Fix strategies
3.1 Preferred method: dos2unix
dos2unix script.sh
Install:
sudo apt install -y dos2unix
# or
sudo dnf install -y dos2unix
3.2 Universal fallback: sed
sed -i 's/\r$//' script.sh
3.3 Temporary output pipeline: tr
tr -d '\r' < script.sh > script.sh.lf && mv script.sh.lf script.sh
4) Bulk conversion with guardrails
find . -type f \( -name "*.sh" -o -name "*.bash" -o -name "*.env" \) -print0 \
| xargs -0 dos2unix
Post-fix verification:
find . -type f -name "*.sh" -exec file {} \; | grep -i crlf && echo "CRLF still present" || echo "all scripts are LF"
5) Permanent prevention (DevOps controls)
5.1 .editorconfig
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
5.2 .gitattributes
* text=auto
*.sh text eol=lf
*.bash text eol=lf
*.env text eol=lf
5.3 Git workstation baseline
git config --global core.autocrlf input
git config --global core.eol lf
5.4 CI gate for CRLF
#!/usr/bin/env bash
set -euo pipefail
if git ls-files '*.sh' '*.bash' | xargs file | grep -qi 'CRLF'; then
echo "ERROR: CRLF detected in shell scripts"
git ls-files '*.sh' '*.bash' | xargs file | grep -i 'CRLF'
exit 1
fi
echo "OK: all shell scripts use LF"
6) Fast incident runbook
file deploy.sh
cat -v deploy.sh | head
dos2unix deploy.sh
chmod +x deploy.sh
bash -n deploy.sh
bash -x deploy.sh
7) Technical conclusion
\$'\\r': command not found is not a Bash syntax issue. It is an engineering process issue across editor settings, VCS normalization, and CI checks.
Once LF is enforced at source and validated in pipeline, this incident class disappears from production.
This post is licensed under CC BY-NC.
Comments
Join the discussion below.
Comments are not configured yet. Add Cusdis settings in /assets/json/config/blog-comments-config.json.