Linux / Bash

How to Resolve "bash: ./script.sh: /bin/bash^M: bad interpreter"

4 min read by DebuggedIt

Quick answer

You try to run a shell script and bash refuses, blaming a broken interpreter path with a strange ^M character in it. The script's shebang line looks completely...

You try to run a shell script and bash refuses, blaming a broken interpreter path with a strange ^M character in it. The script's shebang line looks completely normal when you open it β€” the actual problem is invisible line-ending characters left over from editing the file on Windows.

The Problem

Running the script fails immediately, before any of your actual commands execute:

$ ./script.sh
bash: ./script.sh: /bin/bash^M: bad interpreter: No such file or directory

Opening the file in a normal editor shows nothing wrong β€” the shebang line reads exactly as expected:

#!/bin/bash
echo "Starting deployment..."

Using cat -A to reveal hidden characters exposes what's actually there:

$ cat -A script.sh | head -1
#!/bin/bash^M$

That trailing ^M before the line-ending $ is a carriage return character that shouldn't be there.

Why It Happens

Windows text editors traditionally end lines with two characters β€” carriage return and line feed (\r\n, shown as ^M plus a newline) β€” while Unix and Linux use just a line feed (\n). When a script written or edited on Windows gets copied to a Linux system without conversion, every line keeps its trailing \r. On the shebang line specifically, that stray \r becomes part of the interpreter path bash tries to execute β€” instead of /bin/bash, the kernel is asked to run /bin/bash\r, which doesn't exist. This typically happens when:

  • The script was written or edited in Notepad, an older version of Notepad++, or another Windows-native editor without explicitly setting Unix line endings.
  • The file was transferred via a method that doesn't preserve line endings correctly β€” certain FTP clients in "ASCII mode" misconfigured, or a Git checkout with core.autocrlf set to convert line endings to CRLF.
  • The script was copy-pasted from a Windows application (email, a chat client, a Word document) directly into a Linux terminal or file.
  • A CI/CD pipeline step running on a Windows runner generated or touched the file before it was deployed to a Linux target.

The Fix

The fastest fix is dos2unix, a small utility built exactly for this. Install it if it's not already present:

sudo apt install dos2unix

Run it on the affected script:

dos2unix script.sh
dos2unix: converting file script.sh to Unix format...

Verify the carriage returns are gone:

cat -A script.sh | head -1
#!/bin/bash$

The script should now run normally:

./script.sh

If dos2unix isn't available and you can't install it, sed does the same job without any extra dependency:

sed -i 's/\r$//' script.sh

Another common one-liner using tr strips carriage returns from the whole file:

tr -d '\r' < script.sh > script_fixed.sh
mv script_fixed.sh script.sh
chmod +x script.sh

Still Not Working?

If this keeps happening repeatedly to the same files, especially in a team where some contributors edit on Windows, fix it at the source instead of converting every time. If Git is involved, add a .gitattributes file that forces shell scripts to always use LF line endings regardless of what OS or editor touched them last:

echo "*.sh text eol=lf" >> .gitattributes
git add .gitattributes
git rm --cached -r .
git reset --hard

Also check your global Git config for core.autocrlf, since a setting of true on a Windows machine will actively convert line endings to CRLF on checkout, reintroducing the exact problem you just fixed the next time someone pulls the repo:

git config --global core.autocrlf input

Setting it to input ensures Git converts CRLF to LF on commit but never converts LF back to CRLF on checkout, which keeps shell scripts consistently Unix-formatted no matter which OS a contributor is using.

It's also worth checking your editor's default settings directly if you're the one regularly introducing these line endings. Most modern editors let you set a default line-ending format either globally or per-project, so fixing it at the source means you never have to run dos2unix reactively again. In VS Code, for example, the setting lives under files.eol, and setting it to \n explicitly forces Unix line endings for every new file regardless of the underlying OS:

{
  "files.eol": "\n"
}

If you're not sure whether the problem is isolated to one script or spread across an entire codebase, scan for it across every shell script at once rather than fixing files one at a time as you stumble across them:

grep -rl $'\r' --include="*.sh" .

This lists every shell script in the current directory tree containing a carriage return, which you can then pipe directly into dos2unix for a one-time bulk cleanup:

grep -rl $'\r' --include="*.sh" . | xargs dos2unix