Linux Error Guide: '/bin/bash^M: bad interpreter' — Fix CRLF and Script Run Failures
Fix '/bin/bash^M: bad interpreter: No such file or directory' and 'Permission denied' when running a script on Linux: strip CRLF line endings, chmod +x, and fix shebang and noexec mounts.
- #linux
- #troubleshooting
- #errors
- #bash
Stuck on this Linux Admins error? Get the free incident triage checklist
A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.
Overview
A script that looks perfectly correct can refuse to run for reasons that have nothing to do with its code. The most confusing one is a Windows carriage return smuggled into the shebang line:
./deploy.sh: /bin/bash^M: bad interpreter: No such file or directory
The other everyday failures when launching a script are a missing execute bit and a shebang pointing at an interpreter that is not where the script says it is:
bash: ./deploy.sh: Permission denied
./deploy.sh: /usr/bin/python: bad interpreter: No such file or directory
The ^M in the first message is the giveaway: it is the CR (\r) of a \r\n (CRLF) line ending, making the kernel look for an interpreter literally named /bin/bash\r, which does not exist.
Symptoms
bad interpreter: No such file or directorywith a trailing^Mon the interpreter name.Permission deniedwhen running./script.sh, even thoughcatshows correct contents.- The script runs fine with
bash script.shbut fails with./script.sh. - The script was edited on Windows, pulled from a repo with
autocrlf, or downloaded through a browser. bad interpreternaming a real path (e.g./usr/bin/python) that does not exist on this host.
Common Root Causes
- CRLF line endings — the file has Windows
\r\nendings; the\rbecomes part of the shebang, so the interpreter path is wrong. This is the^Mcase. - Missing execute permission — the script lacks the
xbit, so direct execution (./script.sh) fails withPermission deniedeven though the interpreter is fine. - Wrong or non-portable shebang —
#!/usr/bin/pythonwhen the binary is/usr/bin/python3, or a hard-coded path that differs across distros;env-based shebangs are more portable. noexecmount — the script lives on a filesystem mountednoexec(common for/tmp,/dev/shm, or hardened/home), so nothing there can be executed directly.- Missing interpreter — the shebang names an interpreter that is genuinely not installed.
- BOM at the start of the file — a UTF-8 byte-order mark before
#!prevents the shebang from being recognized at all.
Diagnostic Workflow
Confirm whether the file has CRLF endings — file and a hex/cat check both reveal it:
file deploy.sh # reports "with CRLF line terminators"
cat -A deploy.sh | head -n 1 # a trailing ^M$ means CRLF; $ alone is clean
Check the permissions and the exact shebang:
ls -l deploy.sh # look for the x bit (e.g. -rwxr-xr-x)
head -n 1 deploy.sh # the interpreter path in the shebang
Verify the interpreter named in the shebang actually exists:
head -n 1 deploy.sh | sed 's/^#!//' | awk '{print $1}' | xargs -I{} ls -l {}
which bash python3
Check whether the filesystem the script sits on forbids execution:
findmnt -no OPTIONS -T deploy.sh | tr ',' '\n' | grep -x noexec
Example Root Cause Analysis
A deploy script authored in an editor on a Windows laptop, committed to Git, and pulled onto a Linux server fails with ./deploy.sh: /bin/bash^M: bad interpreter: No such file or directory. The ^M after /bin/bash is decisive: running file deploy.sh confirms Bourne-Again shell script, ASCII text executable, with CRLF line terminators. Every line ends in \r\n, and the shebang’s trailing \r made the kernel search for an interpreter named /bin/bash\r, which of course is absent.
The fix was to strip the carriage returns and prevent Git from re-introducing them:
sed -i 's/\r$//' deploy.sh # or: dos2unix deploy.sh
git add --renormalize . # with * text=auto in .gitattributes
After conversion, file reported a clean script and it ran normally. The lesson: bad interpreter with a ^M is never a problem with bash itself — it is a line-ending problem. Convert to Unix (LF) endings with dos2unix/sed, and add a .gitattributes with * text=auto (or configure your editor) so the CRLF never comes back.
Prevention Best Practices
- Add a
.gitattributeswith*.sh text eol=lf(or* text=auto) so scripts are always checked out with LF endings. - Configure editors to save shell scripts with Unix line endings; keep
dos2unixhandy for imported files. - Use portable shebangs like
#!/usr/bin/env bash/#!/usr/bin/env python3instead of hard-coded interpreter paths. - Set the execute bit intentionally (
chmod +x script.sh) and commit it in Git (git update-index --chmod=+x). - Keep executable scripts off
noexecfilesystems; if/tmpisnoexec, run interpreters explicitly (bash /tmp/script.sh) or relocate the script. - Lint scripts in CI with
shellcheckand reject CRLF endings in a pre-commit hook.
Quick Command Reference
file script.sh # detects CRLF line terminators
cat -A script.sh | head -n 1 # ^M$ reveals a carriage return
dos2unix script.sh # convert CRLF -> LF (or: sed -i 's/\r$//')
chmod +x script.sh # add the execute bit
head -n 1 script.sh # inspect the shebang
findmnt -no OPTIONS -T script.sh # is the mount noexec?
bash script.sh # bypass exec-bit/noexec by calling the interpreter
Conclusion
When a script refuses to run, the failure is usually about the file, not the code. A bad interpreter error ending in ^M means CRLF line endings — fix it with dos2unix and lock it out with .gitattributes. A plain Permission denied on ./script.sh means a missing execute bit (chmod +x) or a noexec mount (call the interpreter directly). And a bad interpreter naming a real path means a wrong or missing shebang target — prefer #!/usr/bin/env. Check file, ls -l, the shebang, and the mount options, and these startup failures resolve in seconds.
Fixed it? Get 500 Linux Admins & DevOps AI prompts — free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.
Did this fix your issue?
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.