1

I'm having a weird problem with a Bash script. The variable NumTMPara has the correct value inside the inner loop, but outside of the loop, the value is 0?

Here is my script:

echo "packet.txt"
cat $SRDB_dir/tpcf.dat | sed -e 's/[\t]/;/g' \
| while read my_line
  do
    PID_SPID=$(echo $my_line | cut -f1 -d';')
    TPCF_NAME=$(echo $my_line | cut -f2 -d';')
    NumTMPara=0
    cat $SRDB_dir/plf.dat | sed -e 's/[\t]/;/g' | grep ";$PID_SPID;" \
    | while read my_line2
      do
        PCF_NAME=$(echo $my_line2 | cut -f1 -d';')
        Param_ID=$(cat $destination/tmparam.txt | sed -e 's/[\t]/;/g' | grep ";$PCF_NAME," | cut -f1 -d ';')
        OFFBYTE=$(echo $my_line2 | cut -f3 -d';')
        OFFBIT=$(echo $my_line2 | cut -f4 -d';')

        Myptc=$(grep $PCF_NAME $SRDB_dir/pcf.dat | sed -e 's/[\t]/;/g' | cut -f5 -d';')
        Mypfc=$(grep $PCF_NAME $SRDB_dir/pcf.dat | sed -e 's/[\t]/;/g' | cut -f6 -d';')
        WIDTH=$(get_width $Myptc $Mypfc)

        PCF_RELATED=""
        PCF_DESCR=$(grep "^$PCF_NAME" $SRDB_dir/pcf.dat | sed -e 's/[\t]/;/g' | cut -f2 -d ';')
        let NumTMPara=1+${NumTMPara}

        #here, the value is correctly reported
        echo -e "\t$PCF_NAME\t$Param_ID\t$OFFBYTE\t$OFFBIT\t$WIDTH\t$PCF_RELATED\t$PCF_DESCR \t${NumTMPara}"
        packetligne="\t$PCF_NAME\t$Param_ID\t$OFFBYTE\t$OFFBIT\t$WIDTH\t$PCF_RELATED\t$PCF_DESCR"
      done

    #Why does NumTMPara = 0 ??
    echo -e "$PID_SPID\t$TPCF_NAME\t${NumTMPara}"
  done

Everything is fine...

NCGT0030 14189 16 0 16  TC Packet ID  1
NCGT0040 14190 18 0 16  TC Packet Seq Control  2
NCGT0020 14188 20 0 16  Generic Failure ID  3
NCGB00B4 14074 22 0 32  Data Field Header  4

until this:

10512 YCSR271B 0

Why 0?

ruakh
  • 175,680
  • 26
  • 273
  • 307

1 Answers1

4

The problem is that in a pipeline (command1 | command2 | command3), each command is run in a separate subshell. This means that any variables from the main shell are copied into the each command's execution environment, but any changes that the command makes are not copied back into the main shell's execution environment.

You have a few options, but two main ones:

  • You can restructure your script so that the variable is not being assigned inside a pipeline.
    • For example, instead of command1 | command2 | while ... do ... done, you could write while ... do ... done < <(command1 | command2). This will still run command1 and command2 inside a subshell, but the while-loop will run in the main shell, as you need.
  • You can save the variable to a temporary file that you can read after the pipeline is complete.
ruakh
  • 175,680
  • 26
  • 273
  • 307
  • it works !! thank you ! Do you know where I could get informations about this syntax ? – user1546581 Jul 24 '12 at 08:08
  • @user1546581: The `<(command)` notation is called *process substitution*, and is documented in [§3.5.6 "Process Substitution" of the *Bash Reference Manual*](http://www.gnu.org/software/bash/manual/bashref.html#Process-Substitution). However, I've never seen documentation of the fact that you can write `< <(command)` to take the substituted process's output from standard input. I only know it because I saw someone mention it once in a comment here on StackOverflow. – ruakh Jul 24 '12 at 13:29