9

The output of the command below is weird to me. Why does it not give me back element 5?

$ echo '0,1,2,3,4,5' | while read -d, i; do echo $i; done
0
1
2
3
4

I would expect '5' to be returned as well. Running GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu). Adding a comma works, but my input data does not have a comma. Am I missing something?

terdon
  • 242,166
Karel
  • 1,468

4 Answers4

12

With read, -d is used to terminate the input lines (i.e. not to separate input lines). Your last "line" contains no terminator, so read returns false on EOF and the loop exits (even though the final value was read).

echo '0,1,2,3,4,5' | { while read -d, i; do echo "$i"; done; echo "last value=$i"; }

(Even with -d, read also uses $IFS, absorbing whitespace including the trailing \n on the final value that would appear using other methods such as readarray)

The Bash FAQ discusses this, and how to handle various similar cases:

mr.spuratic
  • 9,901
8

As other answers state, -d is an end-of-line character, not a field separator. You can do

IFS=, read -a fields <<< "1,2,3,4,5"
for i in "${fields[@]}"; do echo "$i"; done
5

From man:

-d delim

The first character of delim is used to terminate the input line, rather than newline.

Your element 5 doesn't have a delimiter (comma), so it won't be read.

Siva
  • 9,077
3

What you're seeing is the same behavior (and for the same reason) as Why does this 'while' loop not recognize the last line?

As in that case, you can modify the behavior by adding an extra test to the loop termination condition, as follows

while read -d, i || [[ -n $i ]]; do ...

Ex.

$ echo '0,1,2,3,4,5' | while read -d, i || [[ -n $i ]]; do echo $i; done
0
1
2
3
4
5
steeldriver
  • 81,074