Desk Clarity

A field guide to everyday digital problems.

Developer tools / Practical guide

Bad Interpreter with ^M: Inspect the Script Line Endings Before Changing Permissions

If an interpreter path ends with ^M, inspect the first line for carriage-return bytes.

By ON Lab2 min readSources checked November 10, 2026 (KST)

If an interpreter path ends with ^M, inspect the first line for carriage-return bytes. Adding execute permission cannot repair a CRLF shebang. Convert a reviewed text copy to LF and check the complete diff before replacing the original.

Reproduce in an empty temporary directory

This isolated example creates only fixture data in its working directory or runs local JavaScript. Run it separately from your project so the named files cannot overwrite real work.

# Disposable fixture only; do not run this over an existing script.
printf '#!/bin/bash\r\nprintf "hello\\n"\r\n' > crlf.sh
od -An -tx1 -N 16 crlf.sh
python3 - <<'PY'
from pathlib import Path
raw = Path('crlf.sh').read_bytes()
assert b'\r\n' in raw
Path('lf.sh').write_bytes(raw.replace(b'\r\n', b'\n'))
assert b'\r' not in Path('lf.sh').read_bytes()
PY
bash -n lf.sh
bash lf.sh

Observed local output

23  21  2f  62  69  6e  2f  62  61  73  68  0d  0a  70  72  69

hello

What to compare

The hex dump shows 0d 0a at the first line ending. The reviewed copy uses 0a. The syntax check and explicit Bash invocation then validate that copy in this fixture. Executing a file directly also involves operating-system interpreter handling; the exact error wording varies. Inspecting bytes avoids relying on how a terminal renders the control character.

Choose the next action

Confirm that the file is a text script and preserve its original before conversion. Use the editor line-ending control or a narrowly scoped byte conversion, then review the diff and run the relevant syntax check. In a repository, establish a deliberate .gitattributes policy for scripts so future checkouts preserve the expected endings. Inspect an unexpected encoding marker separately rather than assuming every interpreter error is CRLF.

Limits and verification

The conversion shown only replaces CRLF pairs in a generated fixture. It does not delete every carriage return from an arbitrary file, modify repository settings or change executable permission. A missing interpreter, wrong architecture and no-execute filesystem can cause different launch failures and require different evidence. The source describes interpreter selection; the byte-level fixture supplies the local line-ending observation.

Related: Printf Misreads Percent Signs in a Variable: Keep the Format Literal.

Isolated example executed on macOS on November 10, 2026 (KST), using Bash 3.2, Python 3 for the byte fixture, and Node v26.5.0. Results describe the example, not your application or every runtime version.

Sources & further reading