I have been trying to use read in a script with issues so I tried some things on the command line.
$ echo "testing 123" | read x ; echo $x
and
$ echo "testing 123" | read -r x ; echo $x
are only producing any output after being invoked the first time after rebooting the machine. I also got into some state where echo was producing "unbound variable" errors.
Can anyone point me in the right direction?
Mike
PS. This has something to do with subshells but I'm not exactly clear what is going on.
If "some program" were cat, would it change any memory or variables in your current shell? Of course not, it doesn't have access to them -- it's a separate process. All commands run after a pipe, including shell built-ins, have to run in a new process.
So read is run in a brand-new, independent shell, which happily sets the variable x in that shell -- then immediately dies, leaving the original shell unchanged.
Try this:
echo A | ( read X ; echo $X )
Here it will work because the read and the echo are grouped in the same shell.
ksh does pipes in the opposite order so "echo | read" will actually work -- but this isn't something you can count on unless you know you have KSH.
It has to, to avoid deadlocks. Which runs first, the echo or the read? Will either of them hang, waiting for the other? Make them independent and it doesn't matter.
$ var1=
$ var2=
$ var3=
$ echo "a:b:c" | { IFS=":"; read var1 var2 var3 ; echo "var1 is: ""$var1"; echo "var2 is: ""$var2"; echo "var3 is: ""$var3"; }
var1 is: a
var2 is: b
var3 is: c
$ var1=
$ var2=
$ var3=
$ IFS=":"; read var1 var2 var3 <<<$(echo "a:b:c"); echo "var1 is: ""$var1"; echo "var2 is: ""$var2"; echo "var3 is: ""$var3"
var1 is: a b c
var2 is:
var3 is:
That is not because of read, but because the right hand side of the pipe is executed in a subshell in bash and some other bourne type shells. It works in ksh though...
--
Note that IFS=":"; read ..... permanently changes IFS to a colon. To use IFS local to the read command (in bash / ksh93 / zsh ) you can use:
IFS=":" read var1 var2 var3 <<< "a:b:c"
After execution of the command the IFS remains unchanged..
I just figured it out. You need to create a temporary FIFO which < will treat as a file.
$ IFS=":" read var1 var2 var3 < <(echo "a:b:c"); echo "var1 is: ""$var1"; echo "var2 is: ""$var2"; echo "var3 is: ""$var3"
var1 is: a
var2 is: b
var3 is: c
Passed this way rather than on a pipe, read does not create a new subshell.
Thanks for the temporary IFS tip. Are there other commands you can combine without ; ?
Again though, this isn't something special -- this works for any command. It sets and exports a variable, any variable, only for a single line. You can do
HTTP_PROXY="proxy:port" wget ...
if you want wget to use a certain proxy, for example.
I am trying to parse a string like "one:two:three:four" into 4 variables. Should be super simple but I ran into that subshell issue. It needs to be robust so that for ":" the first two and 4th variable are null.
I think I have it now.
And where do you get this string? Perhaps it could be parsed in the first place instead of later.
Actually, I didn't know that either.
That's something totally different from what I was showing you -- temporary assignments:
VAR="a" echo asdf
echo $VAR
The second 'echo' doesn't find the value of VAR because it was set for only that line.
(You can't echo $VAR in that line, either, since the line gets evaluated before the value of VAR exists. It's useful for built-in variables, and things you want to export.)