How to Fix CRLF Line Endings on Linux (Bad Interpreter Error)
When a shell script is edited on Windows and then run on Linux, the shebang becomes #!/bin/bash\r - the kernel can't find an interpreter named /bin/bash<CR> and fails with bad interpreter: No such file or directory. Strip the carriage returns with sed or dos2unix and the script runs instantly.
What ^M actually is
The ^M in /bin/bash^M: bad interpreter is a carriage return (\r). On Windows,
lines end with \r\n (CRLF). On Linux, they end with \n (LF only). When a file
crosses platforms with CRLF intact, every line has an invisible \r at its end -
including the shebang.
Diagnose it in seconds:
file start.sh # "...Bourne-Again shell script, CRLF line terminators"
cat -A start.sh | head -3 # shows ^M$ at the end of each line
file names the problem. cat -A makes the ^M visible so you can confirm
every line is affected, not just the first.
Fix it: sed or dos2unix
Either tool removes the \r in place:
# Option 1: sed (available everywhere)
sed -i 's/\r$//' start.sh
# Option 2: dos2unix (cleaner, purpose-built)
dos2unix start.sh
Both rewrite the file with LF-only endings. After either command, file start.sh
should report ASCII text (no mention of CRLF) and the script will run:
bash start.sh
# no more "bad interpreter" error
The other symptom: command not found on a valid command
CRLF doesn't only break shebangs. If a script sources a .env file or sets
variables line-by-line, trailing \r on variable values causes command not found
errors on commands that clearly exist - because the variable expands to
myvalue\r rather than myvalue. The same sed -i 's/\r$//' fix applies.
Prevent it from coming back
The right long-term fix is to stop CRLF from entering the repo in the first place.
A .gitattributes file tells Git how to normalize line endings on commit:
# .gitattributes
* text=auto
*.sh text eol=lf
*.py text eol=lf
text=auto lets Git detect text vs binary. eol=lf on shell and Python files
forces LF in the working tree even on Windows checkouts.
Pair it with .editorconfig so editors enforce the same rule:
# .editorconfig
[*.sh]
end_of_line = lf
[*.py]
end_of_line = lf
With both in place, a Windows contributor's editor saves LF, and Git rejects
CRLF on commit - the ^M: bad interpreter error stops appearing in CI and
production.
Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.
Try it in a workspace →What you'll practice
- Diagnosing CRLF with
fileandcat -A - Stripping carriage returns in place with
sed -i 's/\r$//'ordos2unix - Preventing CRLF with
.gitattributesand.editorconfig
FAQ
What causes '/bin/bash^M: bad interpreter: No such file or directory'?
The script has Windows CRLF line endings. The shebang becomes #!/bin/bash\r and the kernel looks for an interpreter literally named /bin/bash<CR> - which doesn't exist. Strip the carriage returns with sed -i 's/\r$//' script.sh or dos2unix script.sh.
How do I detect CRLF line endings on Linux?
Run file script.sh - it reports 'CRLF line terminators' if the file has Windows endings. cat -A script.sh shows ^M at the end of every affected line.
How do I prevent CRLF from entering a Git repo?
Add a .gitattributes file with * text=auto and *.sh text eol=lf. This tells Git to normalize shell scripts to LF on commit, so Windows-edited files are fixed automatically before they reach CI or production.
Keep learning
Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.
Open the workspace →