Question regarding shells and subshells when a script is run

I have the following script running with nohup on one of my servers:

#!/bin/bash
#\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
#set log number
#i=1
#\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
#Check if log exits, if so incrememnt log number up so we don't clobber
#while [[ -f /tmp/ethtool.$i.dump ]];do let i=i+1;done
#\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
#Location of streamer log
LOGFILE=/opt/VideoServer/logs/streamer_log.txt
#\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
#Before tailing logs, dump current eth information
ethtool -S eth3 > /tmp/ethdump.$(date "+%m.%d.%y.%H.%M").start
cat /proc/net/cxgb3/ofld_dev0/stats >> /tmp/ethdump.$(date "+%m.%d.%y.%H.%M").start
#\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
#Start tailing the log
tail -F $LOGFILE|while read line;do
        line=($line)
        if [[ "${line[2]}" == "E" ]]; then
            echo "${line[*]}" | grep -q "FX_HR_Timer_Sink::restart_timer_"
                if [[ $? -eq 0 ]];then
                        LOGTIME=$(date "+%m.%d.%y.%H.%M")
                        echo "${line[*]}" > /tmp/ethdump.$LOGTIME
                        echo >>  /tmp/ethdump.$LOGTIME
                        echo >>  /tmp/ethdump.$LOGTIME
                        ethtool -S eth3 >>  /tmp/ethdump.$LOGTIME
                        echo >>  /tmp/ethdump.$LOGTIME
                        echo >>  /tmp/ethdump.$LOGTIME
                        cat /proc/net/cxgb3/ofld_dev0/stats >>  /tmp/ethdump.$LOGTIME
                        #let i=i+1
                fi
        fi
    done

All it's doing is watching a log file, and looking for errors (as indicated by E in the third column) and then checking to see if that error is a specific problem we're having, and if so, dumping some information about the interface. The script runs perfectly, and creates the dump files exactly as expected. The one thing I don't understand is why it seems to create two processes when it is run:

[root@p1b021 ~]# ps -ef|grep logWatch.sh
root      6675     1  0 Jul05 ?        00:00:00 /bin/bash ./logWatch.sh
root      6681  6675  0 Jul05 ?        00:00:01 /bin/bash ./logWatch.sh
root      8221  8195  0 09:44 pts/0    00:00:00 grep logWatch.sh

I assume it has something to do with creating subshells when the script is run, but as I (unfortunately) learned most of my scripting and whatnot from trial and error, and not from real book learning, simple things like this escape me.

I've tried to do some reading, and while I understand shells and subshells to a point in theory, I'm having a hard time understanding what's going on in practice. Could anyone give me the idiots explanation of why I'm spawning two processes to do one job? (I know the script isn't running twice at the same time...only one dump file is being created).

echo "${line[*]}" | grep -q "FX_HR_Timer_Sink::restart_timer_"

The "other side" of a pipe is a child process

You are seeing that second shell because bash is spawning it to run the while loop:

tail -F $LOGFILE|while read line;do

This is also the reason why the effects of modifying variables within the while loop are not visible to the rest of the script.

Some shell implementations run the while loop within the current shell, but not bash.

Regards,
Alister

So there's no way of knowing what process is doing what? If one pid were killed, would the other keep running but not do anything, or would it just spawn the process again once the loop went through again.

I had originally wanted to do something more like

if [[ "${line[2]}" == "E" ]] && [[ "${line[7]}" == "FX_HR_Timer_Sink::restart_timer_" ]];then

But I couldn't get it to work. I think it has something to do with the colour coding used in the log files. Had I done it like this, would it have not spawned the second shell?

Is there a better way I could have accomplished this goal? I've only recently started trying to parse logs in real time like this, and aside from learning perl (which I hope to do eventually) I can't think of who better to have accomplished this.

I'm sorry, could you expand on this a bit? I'm not sure what you mean by modifying variables inside the loop...where else could I have done it?

Alister - counterexample:

I believe this contradicts what you said about bash. What you said is true for some other shells, esp. the Bourne shell.

a=1
while read rec
do
  a=$(( $a + 1 ))
done < t.lis
echo $a

output:

3

For variables are processed inside a bash while loop, changes are visible outside the loop.
At least in this example.

isn't it due to external programs usage?

I guess, there is no sub-shell involved in this or I am wrong?

Alister's statement probably meant for sub-shells.

a=1
cat t.lis | while read rec
do
  a=$(( $a + 1 ))
done 
echo $a

output: 1

Also possible, I misunderstood the scenario.

This is one of the subtle differences you need to watch out for when porting shell scripts. Consider a slightly modified version of Jim's example, i.e.

#!/bin/bash 

a=1
cat $0 | while read rec
do
   (( a++ ))
done

echo $a

Bash outputs 1 but ksh93 and zsh output the number of lines in the script file (10)

I should have been more precise in my post. What I said does not refer to all bash while loops, but to bash while loops which read from a pipe, as does the snippet of code which I quoted in my earlier post.

Apologies for any confusion that my lack of precision may have caused.

Regards,
Alister

---------- Post updated at 11:23 PM ---------- Previous update was at 11:10 PM ----------

Absolutely. Also, the differing behaviors are posix-compliant (you did not imply otherwise, just stating it in case anyone else is wondering ;)):

More @ Shell Command Language

The text in red is a pointlessly verbose way of saying, "the standard has nothing to say".

Regards,
Alister