Reputation: 107040
I want to write a shell script in a more top downish design. I've use to do this with Kornshell scripts. I define various functions that may return multiple set of variables (for example, a getopts
function). I can then use these functions in my main program to set the variables I want.
Unfortunately, BASH and Kornshell seem to diverge in the way they handle this instance. Here's a simple script I run in the Kornshell. My function foo
returns four values. I'll read in these four values into variables in my main program:
#!/bin/ksh
function foo {
echo "this:that:the:other"
}
IFS=":"
foo | read one two three four
echo "one = $one"
echo "two = $two"
echo "three = $three"
echo "four = $four"
This produces:
one = this
two = that
three = the
four = other
Works like a charm. Let's try the same program using BASH instead of Kornshell:
#!/bin/bash
function foo {
echo "this:that:the:other"
}
IFS=":"
foo | read one two three four
echo "one = $one"
echo "two = $two"
echo "three = $three"
echo "four = $four"
This produces:
one =
two =
three =
four =
The pipe to the read doesn't work at all. Let's try this as a hereis read:
#!/bin/bash
function foo {
echo "this:that:the:other"
}
IFS=":"
read one two three four<<<$(foo)
echo "one = $one"
echo "two = $two"
echo "three = $three"
echo "four = $four"
I get:
one = this that the other
two =
three =
four =
That allowed $one
to be set, but not the other variables. Strangely, the colons were removed from the string. Let's remove the colons from the return value:
#!/bin/bash
function foo {
echo "this that the other"
}
read one two three four<<<$(foo)
echo "one = $one"
echo "two = $two"
echo "three = $three"
echo "four = $four"
one = this
two = that
three = the
four = other
That does work. Each variable is read in from function foo
.
What am I doing wrong? Why doesn't setting IFS
seem to work the way I would think it would in BASH? Or is there a better way to do this?
Upvotes: 1
Views: 6423
Reputation: 785038
Without using read this will also work in both bash
and ksh
:
IFS=: && set -- `foo`
echo "one = $1"
echo "two = $2"
echo "three = $3"
echo "four = $4"
bts This also works in BASH without creating a sub-shell:
function foo {
echo "this:that:the:other"
}
IFS=":" read one two three four < <(foo)
echo "one = $one"
echo "two = $two"
echo "three = $three"
echo "four = $four"
Upvotes: 1
Reputation: 289545
You have to quote:
#!/bin/bash
function foo {
echo "this:that:the:other"
}
IFS=":"
read one two three four<<<"$(foo)"
^ ^
When executed it returns:
$ ./a
one = this
two = that
three = the
four = other
Regarding this one not working:
#!/bin/bash
...
IFS=":"
foo | read one two three four
echo "one = $one"
I guess it is because IFS
is defined in a different shell than the one having read
. Also, to pipe foo
to read
doesn't seem to be the way to give a string to read
in bash.
To do it, open a new shell, as seen on Bash script, read values from stdin pipe:
$ foo | { IFS=":"; read one two three four; echo "one = $one"; echo "two = $two"; echo "three = $three"; echo "four = $four"; }
one = this
two = that
three = the
four = other
Upvotes: 6