Fixing "bad interpreter" - Shebang and CRLF Issues
What is the "bad interpreter" error?
Conclusion: The shell cannot run the interpreter named on the script's shebang line. Almost every case is either CRLF line endings or a wrong shebang path.
You try to run a shell script and hit an error like this:
$ ./deploy.sh -bash: ./deploy.sh: /bin/bash^M: bad interpreter: No such file or directory
The file exists and is executable, yet it refuses to run. This error means the kernel cannot start the interpreter declared on line 1 (the #!... shebang).
Narrow it to two causes first
- You see
^Min the error → CRLF line endings (most common) - No
^M, but a path like/usr/bin/python3→ the shebang path does not exist
This article works through them in that order.
Note that Permission denied is a different problem: a missing execute bit (chmod +x). It is easy to confuse the two, so for the permission side see Permission denied Fix.
Why does it say "No such file or directory"?
Conclusion: The kernel uses the shebang line verbatim as the interpreter path. A trailing
\rfrom CRLF turns/bin/bashinto/bin/bash\r, a path that does not exist.
The Linux kernel reads the first line starting with #! and treats the text after #! literally, as the absolute path to an interpreter. That is the root of the error.
Files saved on Windows or by some editors use CRLF (\r\n) line endings instead of LF (\n). The shebang line is then read as:
#!/bin/bash\r
this \r (CR) is part of the "path"
So the kernel looks for a file literally named /bin/bash + carriage return. No such file exists, so you get No such file or directory, and the CR shows up as ^M in the message.
Even when the name and path are correct, a single invisible \r breaks it. That is why the line "looks right but won't run."
The shebang path itself can also be wrong. For example, you wrote #!/usr/local/bin/python3 but Python only exists at /usr/bin/python3 on that machine.
How do you tell if CRLF is the cause?
Conclusion: Use
fileto detect the line-ending type andcat -Ato reveal^Mat line ends. Both expose CRLF at a glance.
Observe the cause before fixing anything.
Check line endings with file
$ file deploy.sh
When CRLF is present, you see:
deploy.sh: Bourne-Again shell script, ASCII text executable, with CRLF line terminators
with CRLF line terminators confirms line endings are the cause. A clean file shows only ASCII text executable with no CRLF note.
Reveal line ends with cat -A
$ cat -A deploy.sh | head -3
#!/bin/bash^M$ ^M$ echo "deploy start"^M$
cat -A marks line ends with $ and CR with ^M. If every line ends in ^M$, it is CRLF. A clean file shows just $.
Inspect the shebang line precisely
$ head -1 deploy.sh | od -c
0000000 # ! / b i n / b a s h \r \n
\r \n together means CRLF. \n alone means LF (clean).
How do you fix CRLF line endings?
Conclusion:
dos2unixis the most reliable fix. Without it, usesed -i 's/\r$//'to strip the trailing CR from every line, then re-check withfile.
Option 1: dos2unix (recommended)
$ dos2unix deploy.sh
dos2unix: converting file deploy.sh to Unix format...
A dedicated tool that converts CRLF to LF. Install it with sudo apt install dos2unix on Ubuntu/Debian or sudo dnf install dos2unix on RHEL-based systems.
Option 2: sed (no extra install)
If dos2unix is not available, sed does the job.
$ sed -i 's/\r$//' deploy.sh
s/\r$// means "delete the CR at the end of each line." -i edits the file in place. Use -i.bak to keep a backup if you are cautious.
$ sed -i.bak 's/\r$//' deploy.sh # keeps deploy.sh.bak
With tr -d '\r', tr only reads from standard input, so you cannot redirect back onto the same file (it would be emptied). Always write to a different file.
$ tr -d '\r' < deploy.sh > deploy.unix.sh # OK $ tr -d '\r' < deploy.sh > deploy.sh # WRONG: ends up empty
Option 3: convert in vim
If the file is already open in an editor, fix it in place.
:set fileformat=unix :w
Always confirm the fix
$ file deploy.sh deploy.sh: Bourne-Again shell script, ASCII text executable $ ./deploy.sh deploy start
If the CRLF line terminators note is gone, you are done.
How do you fix a wrong shebang path?
Conclusion: Confirm the interpreter actually exists at the shebang path with
which. For portability, use#!/usr/bin/env bash.
If there is no ^M and the message shows a path like bash: ./run.sh: /usr/local/bin/python3: bad interpreter, then no interpreter lives at that path.
Confirm the interpreter exists
$ head -1 run.sh #!/usr/local/bin/python3 $ which python3 /usr/bin/python3
The shebang points at /usr/local/bin/python3, but the real binary is at /usr/bin/python3. The paths disagree.
Solve it with env
Instead of hardcoding the real path, let env find the interpreter on PATH for better portability.
#!/usr/bin/env python3
#!/usr/bin/env bash
env searches PATH for the interpreter, so the script works whether the binary is in /usr/bin or /usr/local/bin. It is the modern, environment-resilient form.
Note that passing arguments to the interpreter through env is limited (older systems cannot pass multiple arguments). Options like set -euo pipefail belong in the script body, not crammed into the shebang.
How do you prevent it from coming back?
Conclusion: Pin
.shfiles to LF with.gitattributesand set your editor to save as LF. These two steps shut down almost all CRLF reintroduction.
Fixing it once is pointless if CRLF returns on every save or commit. Cut it off at the source.
Pin line endings in Git
Place a .gitattributes at the repository root and pin shell scripts to LF.
*.sh text eol=lf
Now checkouts always use LF, so the file survives edits from Windows machines.
Also check your core.autocrlf setting. On Linux/macOS, input is the safe choice.
$ git config --global core.autocrlf input
core.autocrlf=true is common among Windows developers, but it adds CRLF on checkout, which is a breeding ground for bad interpreter in shell scripts. When you work with scripts, override it explicitly with eol=lf in .gitattributes.
Set your editor to save as LF
- VS Code: click the
CRLFindicator at the bottom-right and switch it toLF. Add an.editorconfigwithend_of_line = lfto automate it - vim: save with
:set fileformat=unix - Windows Notepad: do not use it for shell scripts (it tends to add CRLF)
Final checklist
file script.shshows noCRLF line terminators- The shebang path in
head -1 script.shresolves to a real interpreter viawhich ls -l script.shshows the execute bit (x) is set
With all three in place, bad interpreter will not return.