Linux / Bash

Bash Script Fails With "$'\r': Command Not Found" (CRLF Line Endings)

The $'\r': command not found error is a strong, specific signal of Windows-style line endings (CRLF) in a script meant to run on Linux/Unix, which expects plain LF line endings — the extra carriage return character at the end of each line gets interpreted as part of the command itself, rather than as whitespace to ignore.

The Problem

Running a shell script produces confusing errors that don't correspond to any actual visible mistake in the code:

./script.sh: line 2: $'\r': command not found
./script.sh: line 5: syntax error near unexpected token `$'{\r''

Opening the file in a normal text editor shows what looks like completely correct, normal shell script syntax, which makes the errors especially confusing at first glance.

Why It Happens

Windows text files traditionally end each line with two characters — carriage return and line feed (CRLF, \r\n) — while Unix/Linux systems use just a line feed (LF, \n) alone. Bash on Linux interprets the trailing \r as a literal character rather than a line ending, which corrupts command parsing in specific, predictable ways. This commonly happens when:

  • A script was written or edited on Windows (using an editor that defaults to or preserves CRLF line endings) and then transferred to a Linux system without converting line endings
  • A script was checked into version control from a Windows machine, and Git's own line-ending handling (core.autocrlf) wasn't configured to normalize this during checkout
  • A file was downloaded or copied through a tool or process that silently converts line endings along the way, without the user necessarily realizing it happened

The Fix

First, confirm the file genuinely has CRLF line endings, rather than assuming based on the error alone:

file script.sh
# output includes "with CRLF line terminators" if this is the cause

Convert the file to Unix-style LF line endings using dos2unix, a small dedicated utility for exactly this purpose:

dos2unix script.sh

If dos2unix isn't installed and you can't easily install it, achieve the same result with sed, which is available on virtually every Linux system by default:

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

This strips the trailing carriage return from every line, converting the file to standard Unix line endings without needing any additional tool installed.

To prevent this recurring for files checked into Git, configure Git's line-ending normalization behavior explicitly rather than leaving it to each individual developer's own local Git configuration (which can vary and produce inconsistent results across a team). Add a .gitattributes file specifying that shell scripts should always use LF endings, regardless of what operating system commits them:

# .gitattributes
*.sh text eol=lf

This ensures Git normalizes line endings on checkout consistently for every contributor, preventing a Windows-based team member from accidentally reintroducing CRLF endings into a script even if their own local editor or Git configuration would otherwise produce them.

If you're editing shell scripts directly on Windows regularly (rather than just occasionally), configure your editor to use LF line endings by default for these files specifically, rather than relying on conversion after the fact each time:

# VS Code settings.json - set default for shell scripts specifically
"[shellscript]": {
  "files.eol": "\n"
}

Still Not Working?

If converting the file's line endings resolves the immediate error but the problem keeps recurring on every new script from the same source, check whether the actual root cause is an automated process (a build tool, a file transfer script, an FTP client in text mode) that's silently re-introducing CRLF endings every time the file passes through it — some file transfer tools historically defaulted to "text mode" transfers that automatically convert line endings based on the perceived platform, which can undo a manual fix the very next time a file is transferred through that same tool or process.