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: cannot execute: required file not found
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).
The message above is what bash 5.1 and later print (Ubuntu 22.04+, Debian 11+, RHEL 9+, etc.). On bash 5.0 and earlier (Ubuntu 20.04, Debian 10, RHEL 8, etc.) you instead see the interpreter path and a ^M (CR), like -bash: ./deploy.sh: /bin/bash^M: bad interpreter: No such file or directory. The cause is identical in both cases.
Narrow it to two causes first
It is almost always one of two things:
- CRLF line endings crept in (most common)
- the shebang path does not exist
On bash 5.1 and later both produce the same cannot execute: required file not found, so the error text alone cannot tell them apart. Do not rely on the message; observe the file directly with file and cat -A (below) to decide.
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 it cannot find the interpreter?
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. bash 5.1 and later report this ascannot execute: required file not found; bash 5.0 and earlier report it asNo such file or directory.
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 it fails. bash 5.1 and later print cannot execute: required file not found; bash 5.0 and earlier print No such file or directory and show the CR as ^M (the ^M only appears in the bash 5.0-and-earlier 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.
This is when no interpreter lives at the shebang path. On bash 5.0 and earlier the message shows the offending path, like bash: ./run.sh: /usr/local/bin/python3: bad interpreter. On bash 5.1 and later a missing path and CRLF look identical -- both print cannot execute: required file not found with no path shown. On either version, do not rely on the error text: rule out CRLF with file / cat -A, then confirm the interpreter exists with which.
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.