Re: Stupid shell script question about "read" [SOLVED]
I wrote:
> Could someone tell me why the following works in zsh but not in
> bash/posh/dash?
>
> benjo[3]:~% echo foo bar baz | read a b c
> benjo[4]:~% echo $a $b $c
> foo bar baz
Thanks everyone for the enlightening answers! So just to summarize, the
problem is that the pipeline is treated as a subshell, and so the
variables $a $b and $c are defined within the subshell but not the
"main" shell.
These seem like the best solutions to my problem:
* Bash-specific (i.e. not POSIX-compliant) :
David Kirchner wrote:
> I'm not sure of the POSIX way to use read in this manner, but I found
> this on Google/A9:
>
> http://linuxgazette.net/issue57/tag/1.html
>
> The example he gives, with the < <() syntax, worked in bash, but not
> in Debian or FreeBSD's /bin/sh.
Almut Behrens wrote:
> In more recent bashes, the following should work as well
>
> #!/bin/bash
> read a b c <<<`echo foo bar baz`
> echo $a $b $c
>
> The <<< ("here strings") are an extension of the "here document" syntax,
> IOW, the string given after <<< is supplied as stdin to the command.
* POSIX-compliant:
Bill Marcum wrote:
> I think the POSIX way would be
> echo foo bar baz | { read a b c; echo $a $b $c; }
Not too bad if what you want to do inside the { } braces is pretty short.
Almut Behrens wrote:
> #!/bin/sh
> eval `echo foo bar baz | (read a b c; echo "a='$a';b='$b';c='$c'" )`
> echo $a $b $c
>
> To get the variable's values from the subshell back to the main shell,
> a shell code fragment is written on stdout, captured with backticks,
> and then eval'ed in the main shell... (this is the moment when I
> usually switch to some other scripting language -- if not before :)
Ugh. Does get the job done though. I guess one has to be a little
careful about escaping special characters in this case? Here's the
safest version I've found so far -- single quotes in the input have to
be special cased with the sed command, and the -r flag to "read" keeps
it from eating backslashes.
# set some variables to nightmarish values for testing purposes
d='"ab\""q"' # literal value is "ab\""q"
e='$d' # literal value is $d
f="'ba'r'" # literal value is 'ba'r'
# here's the meat of the code
result="`echo "$d $e $f" | sed "s/'/\'\\\\\\\'\'/g" | \
( read -r a b c; echo "a='$a' ; b='$b' ; c='$c'" )`"
eval "$result"
# test that $a $b $c have the right values
echo "$a $b $c"
Tested on Sarge with zsh, bash, dash and posh :-)
Of course, replace this:
echo "$d $e $f"
with whatever is producing the output that needs to be put into $a $b $c
Personally, I'd rather constrain my script to work only with bash and
use <<< or < <() operators than to write something like the above!
(N.B. every method above still needs the -r flag to read if the input
might contain backslashes.)
best regards,
--
Kevin B. McCarty <kmccarty@princeton.edu> Physics Department
WWW: http://www.princeton.edu/~kmccarty/ Princeton University
GPG: public key ID 4F83C751 Princeton, NJ 08544
Reply to: