Reading from a pipe (bash)

Probably every bash programmer [1] has at least once stumbled across this problem:
a=1 b=2
echo "4711 4712" | read a b
echo "a=$a b=$b"        # Output: a=1 b=2
Generally spoken, the output of a command (in the example: echo) is to be read into one or more variables. With the approach shown above, however, the variable settings remain unchanged. The reason is that bash executes every part of a pipe in a subshell, including the last one. So a and b don't belong to the actual shell, but are local variables of the last subshell. Other shells, such as ksh and zsh, execute the last part in the actual shell, so the problem doesn't occur there.

One method to work around this annoyance is to keep all processing of the variables within the last subshell:

some_command | 
    read a b
    do_something_with $a $b
    # usw.
This makes sense only in relatively simple cases. A more generally applicable and commonly used method is the following:
read a b <<EOF
Or (provided the shell supports here-strings):
read a b <<<$(some_command)
Here, the output of the command is inserted by command replacement into the actual shell, and then read as a here-document or a here-string. The variables used belong to the actual shell.

The following little function readfrom works similar, but is somewhat more general. The example from above would look like this:

readfrom "$text" a b
readfrom "$(some_command)" a b
With the option -tc the separator c is used instead of $IFS. Example:
readfrom -t: "$(grep "JohnDoe" /etc/passwd)" user x uid gid

[1] Present company excepted, of course

Created 2011-08-11 by mopcoge