Subject: Re: Variables in Loops in Scripts
To: C Kane <ckane@best.com>
From: Roger Brooks <R.S.Brooks@liverpool.ac.uk>
List: netbsd-help
Date: 01/20/1999 11:23:00
On Tue, 19 Jan 1999, C Kane wrote:

>I have a simple sh/ksh script which works okay under
>Solaris and HP-UX, but fails in the same way under
>NetBSD and Linux.  After the "do" loop is over, the
>variables that are set inside the loop revert to their
>previous before-the-loop values.  What am I doing wrong
>in the following script?
>
>#!/bin/ksh
>
>ps -ax | while read PID TT STAT TIME COMMAND ARGS
>do
>  case $COMMAND in
>    syslogd  ) SYSLOGD=$PID ;;
>    portmap  ) PORTMAP=$PID ;;
>    update   ) UPDATE=$PID ;;
>  esac
>  echo syslogd=$SYSLOGD portmap=$PORTMAP update=$UPDATE
>done
>
>echo FINAL syslogd=$SYSLOGD portmap=$PORTMAP update=$UPDATE

It's due to subtle (but crucial) differences between the NetBSD ksh and
the "official" USL version when the last component in a pipeline is a
shell builtin.  USL ksh runs the builtin in the parent process, whereas
NetBSD ksh runs it in a child.  I hit a similar problem a few months ago
with something like:

    print $foo | IFS=: read a b c
    print $a $b $c

The read runs in a child, so the second print doesn't output anything.
I produced a fix which cured this problem, and in poring over the
code I realised there was a similar problem if the last component in
a pipeline was a loop.  I *think* I fixed that as well, but wasn't sure
I'd done enough testing to submit the patch.  There's also the question
of constructs like:

    somecommand | if ... fi

and

    somecommand | case ... esac

where shell builtins within the if ... fi (or case ... esac) ought to run
in the parent shell.  Furthermore, I didn't test the behaviour of all the
other shell builtins which should always be run in the parent shell (and
which could feasibly appear in a compound command at the end of a pipeline).

I'll try your example on my patched ksh and see if it works, although I
doubt I'll have time to do it until the weekend.  Meanwhile, a quick fix
is to put the output from ps into a temporary file and redirect the input
of the do loop from this (yuk!).

AFAIK, always running the last component of a pipeline in a child is
correct behaviour for the Bourne shell.  It's one of the differences
between sh and ksh.



Roger

------------------------------------------------------------------------------
Roger Brooks (Systems Programmer),          |  Email: R.S.Brooks@liv.ac.uk
Computing Services Dept,                    |  Tel:   +44 151 794 4441
The University of Liverpool,                |  Fax:   +44 151 794 4442
PO Box 147, Liverpool L69 3BX, UK           | 
------------------------------------------------------------------------------