Skip to content
Shell & TerminalFix Published Updated 3 min readViews unavailable

Fixing CRLF Line Endings in Shell Scripts

Errors like bad interpreter and $' ': command not found mean carriage returns reached a Unix shell script — confirm bytes, convert, enforce Git policy.

A script copied from a Windows-oriented editor may contain CRLF line endings: carriage return followed by line feed. Unix tools normally expect LF. The extra carriage return is invisible in many editors but remains part of tokens the shell parses.

Recognize the signatures

/usr/bin/env: 'bash\r': No such file or directory
$'\r': command not found
syntax error near unexpected token `$'do\r''

In the first case, the kernel reads the shebang interpreter as bash\r, which is a different, nonexistent filename. Confirm the bytes instead of guessing:

file script.sh
sed -n '1,5l' script.sh
hexdump -C script.sh | head

sed -n l makes carriage returns visible as \r on many implementations.

Convert a copy, then verify

If the whole file is known to be text with CRLF endings:

tr -d '\r' <script.sh >script.fixed.sh

This simple command removes every carriage return, including any intentional one inside data. Tools such as dos2unix apply line-ending-aware conversions and are clearer when available. Compare the result, retain executable permissions as needed, and run the project’s tests before replacement.

Do not run a recursive conversion across a repository containing binary files. Select files by known type or let version-control attributes define the policy.

Prevent recurrence with Git attributes

A repository can state that shell sources are normalized to LF:

*.sh text eol=lf

After adding .gitattributes, use Git’s documented renormalization workflow in a clean branch and inspect the resulting diff. Editor configuration should also save shell scripts as LF, but repository policy is the shared enforcement point.

Line endings are separate from executable permission and encoding. A converted script can still fail because its executable bit is absent, its shebang points to an unavailable interpreter, or it contains invalid syntax. Fix the confirmed byte-level problem, then diagnose any remaining error independently.

Where the two-character convention actually comes from

CR and LF encode two genuinely separate physical actions inherited from mechanical teletype hardware: carriage return moved the print head back to the start of the line, while line feed advanced the paper by one line — a teletype needed both instructions because the print head physically couldn’t return from the far right edge back to column one instantly, so a character printed immediately after CR alone would smear mid-page while the carriage was still moving. Early systems built around teletype terminals adopted the CR+LF pair to match that real hardware constraint, and MS-DOS inherited the same two-character convention from CP/M, which is the direct lineage explaining why Windows text files still use CRLF today while Unix systems, which never carried that specific teletype legacy forward into their own newline convention, settled on LF alone.

Enforcing the same policy at the editor level, not just in Git

# .editorconfig
[*.sh]
end_of_line = lf

A .editorconfig file with end_of_line = lf for shell script patterns is recognized by every editor with EditorConfig support (built into most modern editors and IDEs, or available as a plugin for others) and stops CRLF from being introduced at the point a file is actually saved — a genuinely earlier and more reliable intervention point than catching it later at commit time via .gitattributes. Using both together (.editorconfig for the editing environment, .gitattributes for the repository’s own enforcement and any older-editor gaps) covers the problem from two independent angles rather than relying on either alone — one prevents the mistake at the point of authorship, the other catches anything that slips through regardless of which editor or tool actually wrote the file. Neither one fully substitutes for the other in a real team repository where contributors use a genuine mix of editors and habits. Related: How to Write Robust, Portable POSIX Shell Scripts · How to Build a Reusable Shell Function Library

Sources: