2011-06-15

Read multiple variables from stdin in a bash script

I procrastinated to look up this for such a long time.. today was enough. What I want to achieve is reading a line from stdin and split it among several variables, naively I'd do:

$ echo "A BB CCC" | read a b c
$ echo $a $b $c


(no output.) As well explained here the above code doesn't work, but this one does:

$ read a b c < <(echo "A BB CCC")
$ echo $a $b $c
A BB CCC

as a hint, <(cmds) is called process substitution.

7 comments:

gebi said...

% echo "A BB CCC" | read a b c; echo "$a-$b-$c"
A-BB-CCC

zsh

Javi said...

read a b c <<<"A B C"

gebi: From bash 2.0.5b

josch said...

now someone has just to find a way how to do this with posix compatible with sh - because there is no process substitution or here documents in posix

Javi said...

josch,

Maybe a quick and dirty perl script with eval' evil :-) magick:

eval $(echo "a b c" | perl -ne '@vars=qw(a b c); s/((?{$a=$vars[$b]; $b=$b+1;})\w+) ?/$a=\1\n/g; print')

Is this posix ?? (I don't know) but it can replace read builtin function (in its basic call, of course)

boran said...

How about the old trick with set?:
set -- `echo "A BB CCC"`
echo $1 $2 $3

seanius said...

very simple and posix friendly solution:

echo "A BB CCC" | (
read a b c
echo $a $b $c
)

this makes sure read/echo are in the same subshell. alternatively, you could/should use a function:

foo() {
read a b c
echo $a $b $c
}

echo A BB CCC | foo

i'd advise against using set, at least unless you're absolutely sure about your input, since it might not do exactly what you want it to do in some cases.

mirabilos said...

read a b c <<<"A B C" # is more portable, but also only to modern shells.

echo "A B C" |& read -p a b c # only works on Korn shells, not GNU bash…

Something like this ought to work in POSIX shells:

read a b c <<EOF
A B C
EOF

Feel free to say hi in either #!/bin/mksh (yes that’s a channel name) or #ksh in Freenode IRC.