TL;DR
Your shell script has Windows-style line endings (CRLF) instead of Unix-style (LF). The ^M in the error is a carriage return character (\r) stuck to the end of /bin/bash, making the shebang unreadable. Fix it with:
sed -i '' 's/\r//' script.sh
Or with dos2unix if you have it:
dos2unix script.sh
Then run your script again.
What's Actually Happening
When you see this error:
bash: ./script.sh: /bin/bash^M: bad interpreter: No such file or directory
That ^M is not some cryptic flag. It's a literal carriage return (\r, ASCII 13) sitting at the end of your shebang line. To macOS, the first line of your script looks like this:
#!/bin/bash\r\n
The kernel tries to find an interpreter at the path /bin/bash\r. That path doesn't exist. Script won't run.
Scripts pick up CRLF when created or edited on Windows โ Notepad, VS Code with its default CRLF setting, or WSL with misconfigured git โ and then land on macOS. Windows terminates lines with \r\n. Unix and macOS expect just \n.
Four common ways it gets in:
- Cloning a repo where
.gitattributesforces CRLF checkout - Copying a script from a Windows machine via USB or file share
- Editing in VS Code with
files.eolset to\r\n - Downloading a script from a site that stored it with CRLF
Verify the Problem First
Confirm CRLF is actually the issue before touching anything:
cat -A script.sh | head -5
Lines ending in ^M$ mean CRLF. A clean Unix file just shows $.
The file command gives you a plain-English answer:
file script.sh
# With CRLF: script.sh: Bourne-Again shell script, ASCII text executable, with CRLF line terminators
# Clean: script.sh: Bourne-Again shell script, ASCII text executable
For a raw byte view, xxd shows exactly what's there:
xxd script.sh | head -3
CRLF shows up as 0d 0a pairs. Unix line endings are just 0a.
Fix Approaches
Option 1: sed (built into macOS, no install needed)
sed -i '' 's/\r//' script.sh
The -i '' syntax is macOS-specific โ GNU sed on Linux drops the empty string and uses just -i. This strips every \r in-place.
Option 2: dos2unix (the right tool for the job)
Install it first:
brew install dos2unix
Then convert:
dos2unix script.sh
Got a whole directory of shell scripts? One command handles all of them:
find . -name "*.sh" -exec dos2unix {} \;
Option 3: tr (another built-in option)
tr -d '\r' script_fixed.sh
mv script_fixed.sh script.sh
tr can't edit files in-place, so you need the intermediate file dance.
Option 4: Fix it in VS Code before committing
Click CRLF in the bottom-right corner of VS Code and switch it to LF, then save. To make LF the permanent default for all new files, add this to your VS Code settings:
{
"files.eol": "\n"
}
Option 5: Fix at the git level (prevent recurrence)
If CRLF keeps reappearing after every checkout, git is converting line endings behind the scenes. Stop it:
# For this repo only
git config core.autocrlf false
# Or globally
git config --global core.autocrlf input
The more durable fix is a .gitattributes file checked into the repo itself:
# .gitattributes
*.sh text eol=lf
*.bash text eol=lf
Commit this once, and every contributor โ Windows, macOS, Linux โ gets LF-only line endings on checkout. No per-machine config required.
Verify the Fix Worked
Run the same check as before:
file script.sh
# Should show: ASCII text executable (no CRLF mention)
Make the script executable and run it:
chmod +x script.sh
./script.sh
The /bin/bash^M: bad interpreter error is gone.
Quick Reference
- Symptom:
^Min error message, script won't execute - Cause: CRLF line endings from Windows editors or misconfigured git
- Fastest fix:
sed -i '' 's/\r//' script.sh(no install needed) - Best long-term fix:
.gitattributeswith*.sh text eol=lf

