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
  • 234,489
  • 66
  • 447
  • 667
Karel
  • 1,438
  • 3
  • 13
  • 22

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,721
  • 26
  • 41
  • 8
    I guess one could do `read -d, i || [[ -n $i ]]` a la [What does `while read -r line || [[ -n $line ]]` mean?](https://unix.stackexchange.com/questions/478720/what-does-while-read-r-line-n-line-mean) – steeldriver May 17 '19 at 11:18
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,017
  • 8
  • 56
  • 86
  • So the best fix is putting another comma after the input? – Karel May 17 '19 at 11:09
  • 3
    The [best fix might be to process data with something other than a shell](https://unix.stackexchange.com/q/169716/117549). Since the answerers here have addressed the current question, you might consider a separate question that demonstrates your larger goal. – Jeff Schaller May 17 '19 at 11:15
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
  • 78,509
  • 12
  • 109
  • 152