I’ve been trying to read input into environment variables from program output like this:
echo first second | read A B ; echo $A-$B
And the result is:
-
Both A and B are always empty. I read about bash executing piped commands in sub-shell and that basically preventing one from piping input to read. However, the following:
echo first second | while read A B ; do echo $A-$B ; done
Seems to work, the result is:
first-second
Can someone please explain what is the logic here? Is it that the commands inside the while … done construct are actually executed in the same shell as echo and not in a sub-shell?
How to do a loop against stdin and get result stored in a variable
Under bash (and other shell also), when you pipe something to another command via
|, you will implicitly create a fork, a subshell that is a child of current session. The subshell can’t affect current session’s environment.So this:
won’t give expected result! :
Where computed TOTAL could’nt be reused in main script.
Inverting the fork
By using bash Process Substitution, Here Documents or Here Strings, you could inverse the fork:
Here strings
Here Documents
outside of the loop:
Here Commands
Now you could use
$TOTALin your main script.Piping to a command list
But for working only against stdin, you may create a kind of script into the fork:
Will give:
Note:
$TOTALcould not be used in main script (after last right curly bracket}).Using lastpipe bash option
As @CharlesDuffy correctly pointed out, there is a bash option used to change this behaviour. But for this, we have to first disable job control:
This will work, but I (personally) don’t like this because this is not standard and won’t help to make script readable. Also disabling job control seem expensive for accessing this behaviour.
Note: Job control is enabled by default only in interactive sessions. So
set +mis not required in normal scripts.So forgotten
set +min a script would create different behaviours if run in a console or if run in a script. This will not going to make this easy to understand or to debug…