Bug 24228

Summary: when using /bin/sh, setting variable after pipeline won't work
Product: Base System Reporter: imura <imura>
Component: binAssignee: freebsd-bugs (Nobody) <bugs>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: 4.2-STABLE   
Hardware: Any   
OS: Any   

Description imura 2001-01-10 16:40:00 UTC
When using /bin/sh, setting variables after "|"
(in other words, I mean setting variables in a pipeline sequence),
will be ignored out of the pipeline sequence.

If it is a definition of /bin/sh, it's ok, I'm sorry,
otherwise I think it should be fixed.

Fix: 

Sorry, this is only a information of my problem.
How-To-Repeat: 
write a simple shell script like this:
-----------starts here----------
#/bin/sh

cat FILE | while read line ; do
	if [ $line = foo ]; then
		variable=bar
	fi
	echo $variable   <---- this will be printed
done

echo $variable           <---- this will NOT be printed
-----------ends here------------
Comment 1 Peter Pentchev 2001-01-14 10:47:51 UTC
On Thu, Jan 11, 2001 at 01:32:48AM +0900, imura@af.airnet.ne.jp wrote:
> 
> >Number:         24228
> >Category:       bin
> >Synopsis:       when using /bin/sh, setting variable after pipeline won't work
> >Originator:     Ryuichiro Imura
> >Release:        FreeBSD 4.2-STABLE i386
> >Environment:
> 
> FreeBSD 4.2-STABLE
> 
> >Description:
> 
> When using /bin/sh, setting variables after "|"
> (in other words, I mean setting variables in a pipeline sequence),
> will be ignored out of the pipeline sequence.
> 
> If it is a definition of /bin/sh, it's ok, I'm sorry,
> otherwise I think it should be fixed.

AFAIK, yes, this is expected sh(1) behavior.  From the manpage:

     Note that unlike some other shells, sh executes each process in the
     pipeline as a child of the sh process.  Shell builtin commands are the
     exception to this rule.  They are executed in the current shell, although
     they do not affect its environment when used in pipelines.

That is, sh(1) forks a subshell for each subsequent command/construct
in the pipe, and variable assignments within a construct are only valid
for the subshell that construct is executing in, and not back-propagated
to the main shell.

Thus, in your example, 'variable' would only be set to 'bar' for
the subshell executing the while loop.  See below for a suggestion
for a workaround.

> >How-To-Repeat:
> 
> write a simple shell script like this:
> -----------starts here----------
> #/bin/sh
> 
> cat FILE | while read line ; do
> 	if [ $line = foo ]; then
> 		variable=bar
> 	fi
> 	echo $variable   <---- this will be printed
> done
> 
> echo $variable           <---- this will NOT be printed
> -----------ends here------------

You may do better with something like the following:

#!/bin/sh
# (make sure that's #!, not just # ;)

exec < FILE

while read line; do
	if [ "$line" = "foo" ]; then
		variable="bar"
	fi
	echo "line is $line, variable is $variable"
done

echo "after the loop, variable is $variable"

Hope that makes things clearer :)

G'luck,
Peter

-- 
Hey, out there - is it *you* reading me, or is it someone else?
Comment 2 imura 2001-01-14 15:49:26 UTC
On Sun, Jan 14, 2001 at 12:47:51PM +0200, Peter Pentchev wrote:
> AFAIK, yes, this is expected sh(1) behavior.  From the manpage:
> 
>      Note that unlike some other shells, sh executes each process in the
>      pipeline as a child of the sh process.  Shell builtin commands are the
>      exception to this rule.  They are executed in the current shell, although
>      they do not affect its environment when used in pipelines.
> 
> That is, sh(1) forks a subshell for each subsequent command/construct
> in the pipe, and variable assignments within a construct are only valid
> for the subshell that construct is executing in, and not back-propagated
> to the main shell.
> 
> Thus, in your example, 'variable' would only be set to 'bar' for
> the subshell executing the while loop.  See below for a suggestion
> for a workaround.

Thanks very much for your explanation. I got it. :)
So, please close this PR.

- R. Imura
Comment 3 dwmalone freebsd_committer freebsd_triage 2001-01-14 22:27:33 UTC
State Changed
From-To: open->closed

Satisfactory explaination provided by Peter Pentchev.