You write a script to read a configuration list line by line. The file visibly has two lines, but the last item is never processed. If the final line has no newline, read can put its text in a variable yet return a nonzero status at end of file. A while condition that checks only that status discards the text.
What happens without a final newline?
The basic loop may trim surrounding spaces and skip the newline-free last line. IFS= read -r preserves the input, while an additional condition processes any content still held in line.
First feed two lines without creating a file. The printf input has no \n after last:
printf ' first \nlast' | while read line; do
printf '<%s>\n' "$line"
done<first>The spaces around the first line disappear, and the last line is missing. Default IFS handling accounts for the spaces; the final read reaches end of file without a newline and reports a nonzero status. Seeing both effects at once can make it look as if the input was truncated.
Combine IFS= read -r with a remaining-content check
IFS= preserves leading and trailing spaces, while -r prevents read from interpreting backslashes as escapes. The extra condition processes one last line when read reaches end of file after placing nonempty text in line.
printf ' first \nlast' | while IFS= read -r line || [ -n "$line" ]; do
printf '<%s>\n' "$line"
done< first >
<last>[ -n "$line" ] asks whether nonempty content remains. When the input ends with a newline, read has already processed each line and the final empty variable ends the loop. Without that newline, the remaining text gets one more pass. Also test empty input and newline-terminated input to make sure the loop does not duplicate a line.
When input contains backslashes
The -r option is especially important when text can contain \. Plain read treats a backslash specially. Feed printf '%s\n' 'a\b' through each loop and compare the printed characters to see the difference.
Why does a variable outside the pipe stay unchanged?
The examples use printf ... | while ... to show output. In Bash, the while side of a pipeline may run in a separate shell environment, so do not assume a counter changed inside the loop will retain its value afterward. To read a real file and keep the counter in the current shell, redirect the file into the loop:
count=0
while IFS= read -r line || [ -n "$line" ]; do
count=$((count + 1))
done < items.txt
printf 'processed=%s\n' "$count"This is a line-oriented plain-text pattern. IFS alone cannot parse real CSV with embedded newlines or commas in quoted fields; use a CSV parser for that. Replace items.txt with a file you intend to read, and test a small sample before applying the loop broadly.
Key takeaways
If a while read loop skips the last line, check whether that line has a newline. IFS= read -r line || [ -n "$line" ] preserves surrounding spaces, backslashes, and a nonempty final line without a newline. When reading through a pipe, also check whether the loop's shell environment affects state you need afterward.

