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.

Fullstack Engineerfullstacklinuxline-endings

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

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

Fix a React useState Stale-State BugFullstack projectFix a Blank React Dashboard (Failed Fetch)Fullstack projectFix the React Search That Won't Filter (useEffect Deps)Fullstack projectFullstack roadmapStep by step to hiredFullstack interview questionsSTAR answersAll Fullstack projectsProjects hub

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 →