14

I'm trying to split a string into two variables (without having to use a while loop):

var="hello:world"
IFS=':' read var1 var2 <<< $var

echo "var1: $var1"
echo "var2: $var2"

but i'm not getting the desired result:

var1: 'hello world'
var2: ''

Could anybody please explain if it's possible to do it this way (or similar way)?

codeforester
  • 39,467
  • 16
  • 112
  • 140
coda
  • 622
  • 1
  • 7
  • 14
  • Note that this is not quite a duplicate; the code above is correct, but affected by a bug in 4.x (fixed in 4.3) that is not addressed in the linked duplicate. – chepner Mar 09 '16 at 12:55
  • Based on that, the appropiate behaviour is to reopen this (I just did) and accepted chepner's answer instead. – fedorqui May 05 '16 at 11:39

2 Answers2

25

This is a bug in Bash 4.2. See chepner's answer for a proper explanation.


It is about quotes. Use:

IFS=':' read var1 var2 <<< "$var"
                           ^    ^

instead of

IFS=':' read var1 var2 <<< $var

See result:

$ IFS=':' read var1 var2 <<< "$var"
$ echo "var1=$var1, var2=$var2"
var1=hello, var2=world

But

$ IFS=':' read var1 var2 <<< $var
$ echo "var1=$var1, var2=$var2"
var1=hello world, var2=
Community
  • 1
  • 1
fedorqui
  • 275,237
  • 103
  • 548
  • 598
  • Great to read that, @coda, quoting is always important. Remember you can accept the answer if your question is already solved! – fedorqui Nov 22 '13 at 12:28
  • +1 This is one I don't understand (I'm not disputing it, just not understanding it). The setting of `IFS` should refer to the environment in which `read` is executed, but `$var` is expanded as part of the input redirection, which should *precede* the execution of `read`. Further, without quoting, it appears that the string is indeed one word, assigned to `var1`, but *then* split, as `var1` gets `hello` and `world`, not `hello:world`. – chepner Nov 22 '13 at 13:13
  • 2
    @chepner I found another funny behaviour: if you do `IFS=':' read var1 var2 <<< hello:bye` then it does split properly, while if it comes from a variable it does not. – fedorqui Nov 22 '13 at 13:16
  • Odd. I've filed a bug report, referencing this question. I noticed that using a here document (which should be equivalent to `... <<< "$var"`) also works. – chepner Nov 22 '13 at 14:24
  • @chepner interesting. With a here document you mean `<<< EOF ... EOF`? Also, could you keep me informed about what they say about the bug? Or a link to it should be perfect. – fedorqui Nov 22 '13 at 14:36
  • 2
    Sure. Right, `< – chepner Nov 22 '13 at 14:41
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/41740/discussion-between-chepner-and-fedorqui) – chepner Nov 22 '13 at 23:37
14

FYI for future readers:

After discussing this with the developers, it appears this is indeed a bug in bash 4.2, and has been fixed in the upcoming 4.3 release. From the devel branch change log

rrrr. Fixed several problems with IFS when it appears in the temporary environment and is used in redirections.

Although it's always a good idea to quote parameter expansions anyway, the OP's code should work as intended without quotes.


Here's an explanation of the bug. With the code

var="hello:world"
IFS=':' read var1 var2 <<< $var

the unquoted $var should be a single word, since it contains no character in the global value of IFS (that is, no white-space). read should then see the string hello:world. Because it received two arguments, it should apply word-splitting using its local value of IFS, producing hello and world which are assigned to var1 and var2, respectively.

The bug is that the here string appears to undergo partial splitting using the "leaked" value of IFS being passed to read. As a result, the string becomes hello world, but is still seen by read as a single word. Since that word does not contain a :, read does not split it into two words, and the entire string is assigned to var1.

In bash 4.3, as documented, the expansion of $var does not undergo word-splitting as the argument to the <<< operator; the code

var="hello:1:2 world"
IFS=: read var1 var2 <<< $var

sets var1 to hello and var2 to 1:2 world.

fedorqui
  • 275,237
  • 103
  • 548
  • 598
chepner
  • 497,756
  • 71
  • 530
  • 681