5

since the default value of the IFS variable is a newline/tab/space, the following code:

while read -r line
do
    echo $line
done <<< "hello\nworld"

outputs:

hello
world

but, if I understand it correctly, I can change it so it will separate it by, comma for example.

so this code:

while IFS=',' read -r part
do
    echo $part
done <<< "hello,world"

but this one just outputs:

hello,world

and I expected it to print the same as the script from before.

what am I missing here?

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
yoyobara
  • 101
  • 1
  • 5
  • 3
    `help read` : "Read a line from the standard input [...] The line is split into fields [...] with any leftover words assigned to the last NAME" – jhnc May 01 '22 at 21:24
  • 1
    `read` is only populating one variable, so no splitting occurs. That said, your first loop should only output `hello\nworld`. Your input string is one line containing the two characters ``\`` and `n`, not two lines. – chepner May 01 '22 at 21:26
  • 1
    @chepner, smells like the OP is running somewhere `echo` 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). – Charles Duffy May 01 '22 at 21:36
  • @yoyobara, ...are you starting your script with `#!/bin/bash` or running it with `bash yourscript`? If your interpreter were something other than bash (for example, if it were `sh`), that would clear up the mystery about `echo` behavior. – Charles Duffy May 02 '22 at 01:02

1 Answers1

6

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> 
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441