[comp.unix.questions] Lost Assignment of var in shell for loop with stdout redirection

dennis@rlgvax.UUCP (Dennis Bednar) (02/04/87)

While writing a shell script, I found an unexpected behavior
that I thought I would share with the net.

In summary, apparently

for i
do
	var=xxx
done >/tmp/tmpfile

causes the shell interpreter to run a sub-shell, and so the value
of $var set in the middle of a for loop is lost if $var is referenced
after the for loop.  IS THIS CONSIDERED TO BE A sh(1) bug?

The first shell script below named "t" fails, in that if run as
"t -A", it prints AFLAG = 0, not AFLAG = 1, at the end, even though
AFLAG is set to 1 in the for loop.  However, if, the
">/tmp/t$$" is removed from the file "t", the problem goes away.
Since I needed to capture the output into a temp file /tmp/t$$,
I fixed it by modifying "t" to save the AFLAG variable in a temp file.
The fixed version I came up with is called "t2".


Here is the output, when run on our machine (cci632 Sys5 r2):
Script started on Tue Feb  3 16:35:35 1987
$ t -A
1: AFLAG = 1
2: AFLAG = 0
$ t2 -A
1: AFLAG = 1
2: AFLAG = 1
$ 
script done on Tue Feb  3 16:35:47 1987

Below are two very short scripts "t" and "t2".


#--------------- CUT HERE ---------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	t
#	t2
# This archive created: Tue Feb  3 16:45:34 EST 1987
#
if test -f t
then
echo shar: will not over-write existing file 't'
else
echo x - t
# ............    F  I   L   E      B  E  G  .......... t
cat << '\SHAR_EOF' > t
AFLAG=0
for i
do
	if [ $i = -A ]
	then
		AFLAG=1
		echo "1: AFLAG = $AFLAG" 1>&2 # to stderr not to tmp file
		continue
	fi
done >/tmp/t$$
echo 2: AFLAG = $AFLAG
rm -f /tmp/t$$
\SHAR_EOF
# ............    F  I   L   E      E  N  D  .......... t
fi # end of overwriting check
if test -f t2
then
echo shar: will not over-write existing file 't2'
else
echo x - t2
# ............    F  I   L   E      B  E  G  .......... t2
cat << '\SHAR_EOF' > t2
AFLAG=0
for i
do
	if [ $i = -A ]
	then
		AFLAG=1
		echo "1: AFLAG = $AFLAG" 1>&2 # to stderr not to tmp file
		echo $AFLAG >/tmp/T$$		# pass flag via file
		continue
	fi
done >/tmp/t$$
AFLAG=`cat /tmp/T$$`
echo 2: AFLAG = $AFLAG
rm -f /tmp/t$$ /tmp/T$$
\SHAR_EOF
# ............    F  I   L   E      E  N  D  .......... t2
fi # end of overwriting check
# end of shell archive
exit 0
-- 
FullName:	Dennis Bednar
UUCP:		{seismo|sundc}!rlgvax!dennis
USMail:		CCI; 11490 Commerce Park Dr.; Reston VA 22091
Telephone:	+1 703 648 3300

dcm@sfsup.UUCP (02/06/87)

In article <354@rlgvax.UUCP> dennis@rlgvax.UUCP writes:
>While writing a shell script, I found an unexpected behavior
>that I thought I would share with the net.
>
>In summary, apparently
>
>for i
>do
>	var=xxx
>done >/tmp/tmpfile
>
>causes the shell interpreter to run a sub-shell, and so the value
>of $var set in the middle of a for loop is lost if $var is referenced
>after the for loop.  IS THIS CONSIDERED TO BE A sh(1) bug?
>
>-- 
>FullName:	Dennis Bednar
>UUCP:		{seismo|sundc}!rlgvax!dennis
>USMail:		CCI; 11490 Commerce Park Dr.; Reston VA 22091
>Telephone:	+1 703 648 3300

Yes, this is a know shell bug.  It also occurs with a structure like
this:

while read var
do
	# anything
done < /tmp/tmpfile

If you happen to have ksh, I'd suggest using that, since this bug has
been fixed there.  Ksh doesn't spawn a new shell to do the redirection.

Anybody have diffs to fix /bin/sh to work this way?


					Dave
--

David C. Miller, consultant
Communications Interface Addresses:
    Paperware:  AT&T Information Systems, 190 River Road, Summit, NJ  07901
    Liveware:  (201) 522-6107
    Software:  {allegra,burl,cbosgd,clyde,ihnp4,ulysses}!sfsup!dcm
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
On good days life is T.A.N.S.T.A.A.F.L.
On days like today:  T.A.N.S.T.A.A.L.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

dce@mips.UUCP (02/06/87)

No, this is not considered to be a bug. When you do redirection or pipes
of any kind, the shell forks. This is done because getting back to the
original file state would be impossible if you tried to do it locally.

Basically, it would be almost impossible to handle the case in which
you execute

	shell_script < foo > bar

if there was some internal redirection inside of the shell script.
The shell couldn't save the file descriptors somewhere else because
it would run out of descriptors (ever hear of the 'fd>file' syntax?).
Thus, the shell has to fork to the the redirection.

Now, if your problem is that you need a way to assign a variable
during a redirection and have that value available afterwards, you have
a couple of possibilities:

	1. Place the variable value in a file, and use

		VAR="`cat file`"

	2. You can get tricky, as in

		#!/bin/sh
		exec 9>&1	# save stdout in fd 9
		exec 1>bar	# stdout->file "bar"
		for i in 1 2 3 4 5
		{
			var="$i"
			echo "hello"  # output goes in file "bar"
		}
		exec 1>&9	# stdout->saved stdout
		echo "$var"

	   I use file descriptor 9 here to stand out, but I believe that
	   all file descriptors other than 0, 1, and 2 are closed upon
	   execution of subprocesses, so you should be able to use anything
	   from 3 to 9 (2-digit fds are syntax errors). Can Guy or Doug
	   or someone verify this?
-- 
			David Elliott

UUCP: 	{decvax,ucbvax,ihnp4}!decwrl!mips!dce, DDD:  	408-720-1700

jerryp@tektools.UUCP (02/06/87)

[I've restricted this followup to comp.unix.questions only.]

In article <354@rlgvax.UUCP> dennis@rlgvax.UUCP (Dennis Bednar) writes:
> In summary, apparently
> 
> for i
> do
> 	var=xxx
> done >/tmp/tmpfile
> 
> causes the shell interpreter to run a sub-shell, and so the value
> of $var set in the middle of a for loop is lost if $var is referenced
> after the for loop.  IS THIS CONSIDERED TO BE A sh(1) bug?

You're right about the loop: it runs in a sub-shell.
(This is true with the Berkeley Bourne shell, at least.)
The same thing happens with redirected input, as in:

	someprog |
	while read line
	do
		...set some shell variables...
	done

shell variables set inside the loop won't be available outside of it.
I think it's a well-known bug [anyone vote for calling it a feature? :-)]

It's fixed, at least partially, in the Korn Shell.

--Jerry Peek, Tektronix, Inc.
US Mail:      MS 74-900, P.O. Box 500, Beaverton, OR 97077
uucp-style:   {allegra,decvax,hplabs,ihnp4,ucbvax}!tektronix!tektools!jerryp
Domain-style: jerryp@tektools.TEK.COM
Phone:        +1 503 627-1603

gwyn@brl-smoke.UUCP (02/08/87)

In article <2171@tektools.TEK.COM> jerryp@tektools.TEK.COM (Jerry Peek) writes:
>shell variables set inside the loop won't be available outside of it.

The /bin/sh distributed with 4.nBSD (for n <= 4, at least) is a really
old, buggy, puny version of the Bourne shell.  I was going to supply a
modern version for 4.3BSD but Berkeley balked at requiring a UNIX
System V source license of their "customers".  The current edition of
the BRL SVR2 shell can be easily configured to replace a 4BSD /bin/sh.
(Info about this was posted not long ago, so I won't repeat it here.)

guy@gorodish.UUCP (02/17/87)

>>shell variables set inside the loop won't be available outside of it.
>
>The /bin/sh distributed with 4.nBSD (for n <= 4, at least) is a really
>old, buggy, puny version of the Bourne shell.

Unfortunately, the new, bug-fixed, whizzy version supplied with
System V, Releases 2 and 3 does the exact same thing.