Answering The "Why" Question
IFS is a field separator, not a record separator. (The default record separator is the newline; it can be changed with the -d
argument to read
). Each call to read
reads a single record, and optionally splits it into multiple variables at field boundaries.
When you run read -r part
, you're reading only one record (the one that goes into part
), so there's no separating to be done: it's thus normal and expected that $part
contains the entire record, commas included.
Alternatives Where IFS Is Honored
By contrast, if you used -a
to specify your destination as an array (read -r -a parts
) or passed more than one destination variable (read -r part1 part2 part3
), splitting into fields would take place.
Compare to:
while IFS=, read -r key value; do
echo "key=$key, value=$value"
done <<EOF
hello,world
hi,neighbor
EOF
Similarly, one can read a line into an array:
while IFS=, read -r -a parts; do # split on comma into array
printf 'Read %d parts: ' "${#parts[@]}" # count items in array
printf '<%s> ' "${parts[@]}" # print those items in arrow brackets
printf '\n'
done <<EOF
three,word,array
this,is,four,words
EOF
...which, as expected, emits:
Read 3 parts: <three> <word> <array>
Read 4 parts: <this> <is> <four> <words>
help read
: "Read a line from the standard input [...] The line is split into fields [...] with any leftover words assigned to the last NAME" – Keslieread
is only populating one variable, so no splitting occurs. That said, your first loop should only outputhello\nworld
. Your input string is one line containing the two characters\
andn
, not two lines. – Feiningerecho
has XPG-like behavior and is replacing the\n
with a newline itself (which is odd, because that's very much not default on bash). – Blackmarketeer#!/bin/bash
or running it withbash yourscript
? If your interpreter were something other than bash (for example, if it weresh
), that would clear up the mystery aboutecho
behavior. – Blackmarketeer