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:

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

    zsh

    ReplyDelete
  2. read a b c <<<"A B C"

    gebi: From bash 2.0.5b

    ReplyDelete
  3. 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

    ReplyDelete
  4. 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)

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

    ReplyDelete
  6. 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.

    ReplyDelete
  7. 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.

    ReplyDelete