[comp.unix.questions] variables set in sh while loop don't get set?

mellman@ttrdd.UUCP (09/24/87)

When I set a variable in a while loop in a sh script, in some scripts
it doesn't seem to get set.  For example, a common thing is to do a

	pipeline | while read proc
	do

and then to test for some condition in a case statement.  Twice now, I
have written scripts where a variable set in the case loop (or anywhere
in the while loop, for that matter) won't have been changed from its
initial value after the while loop terminates.  Can anyone explain to me
why this is so.

For example, I have a little script that will log me off of my 630 terminal
by nohup-ing the login process - as long as only insignificant processes
are still running:

#
# This script will shutdown my account
#

PATH=$HOME/bin:/bin:/usr/bin:/usr/lbin
options="xd"
arglist=""
optlist=""

set -- `getopt $options $*`
if [ $? != 0 ]; then
	echo "Usage: $0 [$options]"
	exit -1
fi

for arg in $*; do
	case $arg in
	--)
		shift
		break
	;;
	-d)
		execute=2
		export execute
		shift
	;;
	-x)
		execute=1
		export execute
		shift
	;;
	esac
done

ps -u$LOGNAME | awk '{print $4}' | while read proc
do
	execute=3
	case $proc in
		sysmon | awk | ksh | ps | layers | COMMAND)
		;;
		"")
			echo '\007\007' defunct process?
			execute=0
			export execute
		;;
		*)
			echo '\007\007' $proc still running
			execute=0
			export execute
		;;
	esac
done

echo execute is $execute

if [ $? -eq 0 ]; then
	if [ ${execute:-0} -eq 1 ]; then
	    kill -1 ${fatherpid:-"fatherpid not set"}
	else
	    echo ${fatherpid:-"fatherpid not set"}
	fi
fi

But, at the echo, it can be seen that execute never got set during the
while loop.  It's driving me crazy.  Any help would be appreciated.

scl@virginia.acc.virginia.edu (Steve Losen) (09/28/87)

In article <212@ttrdd.UUCP> mellman@ttrdd.UUCP (Thomas Mellman) writes:
>When I set a variable in a while loop in a sh script, in some scripts
>it doesn't seem to get set.  For example, a common thing is to do a
>
>	pipeline | while read proc
>	do

	done  # assumed

What is happening is that the while loop is being run as a separate
process.  The shell is very fond of doing this sort of thing,
especially in pipelines.  At the "done" the "while loop process" exits,
giving control back to the parent shell, whose variables haven't
changed.  I suggest that you do this:

pipeline | (
	while read proc
	do
		# set some variables
	done
	# now use the variables set in while loop
)

This forces everything between the parentheses to happen in the child
process, so you can get some use out of those variables.
-- 
Steve Losen
University of Virginia Academic Computing Center

wietse@eurifb.UUCP (Wietse Venema) (09/29/87)

In the Bourne shell for/case/while/if-else-fi constructs are executed by 
a child process when the output or input of such a construct is redirected, 
e.g. when it is part of a pipe.

		Wietse Venema		(uucp:		mcvax!eutwc1!wietse)
					(bitnet:	wswietse@heithe5)

crc@abvax.icd.ab.com (Clive R. Charlwood) (09/29/87)

in article <212@ttrdd.UUCP>, mellman@ttrdd.UUCP (Thomas Mellman) says:
> 
> When I set a variable in a while loop in a sh script, in some scripts
> it doesn't seem to get set.  For example, a common thing is to do a
> 
> 	pipeline | while read proc
> 	do
> 
> and then to test for some condition in a case statement.  Twice now, I
> have written scripts where a variable set in the case loop (or anywhere
> in the while loop, for that matter) won't have been changed from its
> initial value after the while loop terminates.  Can anyone explain to me
> why this is so.

In the bourne shell while/for loops that have stdin redirected are 
implemented as sub shells. Thus any changes to variables 'disapear'
when the shell exits.




Clive

		Clive R. Charlwood @ Allen-Bradley Company
		Industrial Computer Division
		747 Alpha Drive, Highland Heights, OH   44143
		...!{decvax,masscomp,pyramid,cwruecmp}!abic!crc
-- 
		Clive R. Charlwood @ Allen-Bradley Company
		Industrial Computer Division
		747 Alpha Drive, Highland Heights, OH   44143
		...!{decvax,masscomp,pyramid,cwruecmp}!abic!crc

jws@hpcllf.HP.COM (John Stafford x75743) (09/29/87)

Shell script loops with any I/O of the loop as a whole redirected (i.e.
stdin in your case) are run in a separate (sub-)shell process and so
changes in variables in such loops obviously are not reflected in the
parent.  Loops without redirected I/O are run in the current shell and
so changes in variables work.  This is either not documented or
obscurely documented.

I have "gotten around" this feature by writing things into temporary
files and having the parent get them from there:

   # Replace VARIABLE="" with
   temp=/tmp/x$$
   trap "rm -f $temp" 0 1 2 3 15 # Don't leave a mess
   cp /dev/null $temp            # >$temp would work also
   process | while read something
   do
      # Replace VARIABLE="$VARIABLE $something" with
      echo " $something" >>$temp
   done
   VARIABLE="`cat temp`"

It isn't perhaps the cleanest, but it works.

John Stafford -- Hewlett Packard Computer Language Lab
{allegra,decvax,ihnp4,ucbvax}!hplabs!hpclla!jws
{fortune,sun,thirdi,ucbvax}  !hpda  !hpclla!jws