I missed my last two columns (see the first question for details), so let's
dive right in.
Q:
Where'd you disappear to four months ago?
A:
Just as I finished up the
supplementary material
for the
January column
I came down with a nasty case of
GERD
(AKA acid reflux disease), caused by many months of frequent overeating
at the local Chevy's restaurant
(not their fault; I ate plain food, just way too much of it).
I was on
prescription antacids
for three months to give my digestive system a chance to recover.
While I haven't needed any special treatment for a while, a lot of
damaged tissue has been healing itself and this is no fun, trust me.
Much of the time I have felt like I was coming down with a cold, but
it didn't act like a cold; and a few times every month something big
has peeled off and made eating a scary prospect for a few days because
the newly exposed esophagus skin is really sensitive.
I'm not joking here, people; if it weren't for the fact that I'm still in my
late twenties I'd swear I was having a prelude to a heart attack or something.
As you might imagine, my free-time activities have been in disarray for months.
Moral of the story is: don't get heartburn. It's bad.
We now return you to your regularly scheduled AnswerMan...
Q:
Why does my script work on commercial unixes but not on free unixes?
After the "do" loop is over, the variables that are set inside the
loop revert to their previous before-the-loop values.
#!/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
A:
Believe it or not, it's the fact that you're piping something into the
while construct. One of the gray areas of unix shell scripting is
which operations occur within subshells. Subshells are
normally requested explicitly by using parentheses:
(cd path/to/my/dir ; make something)
To implement I/O redirection for complex shell commands, a unix shell will
typically fork copies of itself so that it can redirect the output
of the subshell thus created. This always happens with command substitution,
but in your case (a loop) there is no clear need for it. However, the shell
decides to do it anyway. You could argue (with some basis) that this is a bug
in the shell, but the better solution is to make your script more bulletproof:
#!/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
}
If you ever wondered what the heck a shell does with curly braces, now you know.
Everything inside the curly braces is forced into its own subshell, and once
there the while loop executes as expected.
It is still possible for additional subshells to be created, if the operations
inside the curly braces are complex enough to require it.
Admittedly, this is one of the yuckier areas of unix shell scripting.
Once a construct gets complex enough, you lose the ability to change
variable values in the "top level" shell. If anyone has a good general
technique for combatting this, I'd love to hear it.