read without -r mangles backslashes

Without `-r`, `read` interprets backslashes as line continuations and escape sequences. Always pass `-r`.

Problem

By default, `read` treats backslash as the start of an escape sequence — a line ending in `\` is joined with the next, and `\n` inside data is consumed. Almost no script wants this; the result is unexpected data corruption when input contains paths or JSON.

Bad

while read line; do
  process "$line"
done < input.txt

Good

while IFS= read -r line; do
  process "$line"
done < input.txt

Explanation

`IFS=` prevents trimming of leading/trailing whitespace; `-r` disables backslash interpretation. Together they are the safe default for line-by-line reading.

When It Matters

Without -r, read treats backslash as an escape character: it strips single backslashes and joins a line ending in a backslash with the next one. Any script that reads Windows paths, regexes, LaTeX, JSON with escaped quotes, or password files will silently corrupt its input. The corruption is invisible in the common case — most lines contain no backslash at all — so the bug reaches production and then destroys exactly the one record that had a backslash in it.

Second Example

Note

IFS= read -r line is the canonical form: -r disables escape processing, and the empty IFS stops leading and trailing whitespace being trimmed.

Exceptions

Omitting -r is only correct when you deliberately want line continuation — for example reading a config format where a trailing backslash joins lines. That is rare enough to deserve a comment plus an explicit "# shellcheck disable=SC2162".

Faq

Q

What does IFS= do in front of read?

A

It clears the field separator for that one command, which stops read from trimming leading and trailing whitespace from the line. Without it, indentation and trailing spaces are lost.

Q

Does -r affect how fields are split?

A

No. Field splitting is controlled by IFS; -r only controls whether backslash is treated as an escape character.

Q

Why does my loop skip the last line?

A

read returns non-zero when it hits end of file without a trailing newline, so the final partial line is read but the loop exits. Use while IFS= read -r line || [ -n "$line" ]; do ... done.