Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Linux Admins By James Joyner IV · · 8 min read Last reviewed Jul 2026

Linux Error Guide: '/bin/bash^M: bad interpreter' — Fix CRLF and Script Run Failures

Quick answer

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
Free toolkit

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 directory with a trailing ^M on the interpreter name.
  • Permission denied when running ./script.sh, even though cat shows correct contents.
  • The script runs fine with bash script.sh but fails with ./script.sh.
  • The script was edited on Windows, pulled from a repo with autocrlf, or downloaded through a browser.
  • bad interpreter naming 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\n endings; the \r becomes part of the shebang, so the interpreter path is wrong. This is the ^M case.
  • Missing execute permission — the script lacks the x bit, so direct execution (./script.sh) fails with Permission denied even though the interpreter is fine.
  • Wrong or non-portable shebang#!/usr/bin/python when the binary is /usr/bin/python3, or a hard-coded path that differs across distros; env-based shebangs are more portable.
  • noexec mount — the script lives on a filesystem mounted noexec (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 .gitattributes with *.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 dos2unix handy for imported files.
  • Use portable shebangs like #!/usr/bin/env bash / #!/usr/bin/env python3 instead 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 noexec filesystems; if /tmp is noexec, run interpreters explicitly (bash /tmp/script.sh) or relocate the script.
  • Lint scripts in CI with shellcheck and 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.

Free download · 368-page PDF

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?

Free download · 368-page PDF

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.