Re: Fascinating problem with bash

On 08/24/2010 04:09 AM, Oliver Schneider wrote:
Hello Cameron, Bob,

As soon as I read this paragraph I saw the problem. I confirmed it
looking at the code. It's a common problem.

This construct:

some_cmd | while read var ; do

will result in OTHER_VAR being unset at the completion of the loop. That
is because the while command is on the right-hand side of the pipe
meaning it runs in a subshell. At the end of the while loop, the
subshell exits and any vars it sets will go away with it.
Okay, that is surprising indeed, as SHLVL is not being adjusted to reflect
that fact, according to my findings. But thanks a bunch for pointing that
out. It's surely more elegant to use this method than to write to a

Also thanks to Bob for providing the links. Very useful, noted them down.

Thanks a lot,

// Oliver

All this got me to wondering, so I looked at the two links Bob provided.
And, I did some tests of my own.

First, I think there's an error on the SubShell page, in the "example"
of the difference between a "subshell" and a full "child process", at
the end. The author uses double quotes for the subshell, then single
and double quotes for the child process. It's the single quotes that
prevent evaluation of $a, not the "child process" versus "subshell".

If you use single quotes in the subshell line, the $a is printed as is:

$ (echo 'a is $a in subshell')
a is $a in subshell

Since the double quotes in the child process example are not needed,
removing them and replacing the single quotes with double quotes results
in output with $a replaced by it's value, 1.

$ sh -c "echo a is $a in child"
a is 1 in child

Getting quoting right in shell scripts is often difficult. ;-)

This is the code used for my testing. Note I use double quotes only and
backslashes when I want to "quote" specific single characters to prevent
evaluation. The quoting forces the use of 'eval' in the 'while' loop's
first echo, to force variable substitution to happen when the loop is
run, otherwise the output would be strings, $$ and $SHLVL, literally.


SHLVL=1 # I'm using ksh which is setting this to 2, in GUI env.
# This also means you may not want to trust the value, in some cases.
for n in 1
echo iteration: $n pid1 is $$ SHLVL is $SHLVL
echo $n | while read m
MyVar='while loop'
eval echo "iteration: $m and pid2 is \$$ SHLVL is \$SHLVL"
bash -c "echo parent is $$ I\'m \$$ SHLVL is \$SHLVL"
if [ "$MyVar" ]
echo $MyVar
echo MyVar is empty
done | cat # Just to put the loop between two pipes.
if [ "$MyVar" ]
echo $MyVar
echo MyVar is empty

The results of a run:

iteration: 1 pid1 is 23853 SHLVL is 1
iteration: 1 and pid2 is 23853 SHLVL is 1
parent is 23853 I'm 23857 SHLVL is 2
while loop
MyVar is empty

The only point where SHLVL, and $$, get 'reset', is in the explicit
execution of 'bash -c'.

I believe this suggests modern shells are maintaining the functionality
of a "subshell", but are running things in the "current" process, for
reasons of efficiency.

Or, I'm completely off my rocker (possible) and not getting it (also
possible). If there's a better explanation, I'd like to see it ;)


Bob McGowan

To UNSUBSCRIBE, email to debian-user-REQUEST@xxxxxxxxxxxxxxxx
with a subject of "unsubscribe". Trouble? Contact listmaster@xxxxxxxxxxxxxxxx