Bash variable scope

Please explain to me why the very last echo statement is blank? I expect that XCODE is incremented in the while loop to a value of 1:

#!/bin/bash
OUTPUT="name1 ip ip status" # normally output of another command with multi line output

if [ -z "$OUTPUT" ]
then
        echo "Status WARN: No messages from SMcli"
        exit $STATE_WARNING
else
        echo "$OUTPUT"|while read NAME IP1 IP2 STATUS
        do
                if [ "$STATUS" != "Optimal" ]
                then
                        echo "CRIT: $NAME - $STATUS"
                        echo $((++XCODE))
                else
                        echo "OK: $NAME - $STATUS"
                fi
        done
fi

echo $XCODE

I've tried using the following statement instead of the ++XCODE method

XCODE=`expr $XCODE + 1`

and it too won't print outside of the while statement. I think I'm missing something about variable scope here, but the ol' man page isn't showing it to me.


Because you're piping into the while loop, a sub-shell is created to run the while loop.

Now this child process has its own copy of the environment and can't pass any variables back to its parent (as in any unix process).

Therefore you'll need to restructure so that you're not piping into the loop. Alternatively you could run in a function, for example, and echo the value you want returned from the sub-process.

http://tldp.org/LDP/abs/html/subshells.html#SUBSHELL


The problem is that processes put together with a pipe are executed in subshells (and therefore have their own environment). Whatever happens within the while does not affect anything outside of the pipe.

Your specific example can be solved by rewriting the pipe to

while ... do ... done <<< "$OUTPUT"

or perhaps

while ... do ... done < <(echo "$OUTPUT")

This should work as well (because echo and while are in same subshell):

#!/bin/bash
cat /tmp/randomFile | (while read line
do
    LINE="$LINE $line"
done && echo $LINE )

One more option:

#!/bin/bash
cat /some/file | while read line
do
  var="abc"
  echo $var | xsel -i -p  # redirect stdin to the X primary selection
done
var=$(xsel -o -p)  # redirect back to stdout
echo $var

EDIT: Here, xsel is a requirement (install it). Alternatively, you can use xclip: xclip -i -selection clipboard instead of xsel -i -p