Bash setting a global variable inside a loop and retaining its value -- Or process substituion for dummies

18,451

Solution 1

The important thing to understand is this: child process is born with its own environment and cannot affect the variables of its parent. If you set a variable in a child process, then the value of the variable in the parent is not affected. These are actually two different variables which just happen to have the same name.

The second thing to understand is when bash runs a command as a child process. There are two cases relevant to the question:

  1. Each process connected by a pipe | is a child of the current shell.
  2. Running a single builtin command with a redirection (e.g. <) will not spawn a child process.

Here is a simple session which demonstrates these ideas:

$ somevar=initial
$ echo test1 | read somevar
$ echo $somevar
initial
$ read somevar < <(echo test2)
$ echo $somevar
test2

The first read is a child process and therefore somevar in the main shell does not change. The second read is executed by the main shell itself and hence somevar is updated.

This means that your code will work as you expect unless you add a pipe in front of or after the for loop, i.e. this works as you want it to:

# DEFINE HERE
SOMEVAR=0
DAEMON_COUNT=10

for i in `seq 0 ${DAEMON_COUNT}`;
do
        if [ ! -d "data$i" ]; then
# SET HERE
                SOMEVAR=10
                echo "data$i does not exist. Creating...";
                mkdir data$i
        fi
done

# TEST AND USE HERE
echo ${SOMEVAR}     # This displays 10

Solution 2

I might have misundestood but...

bool=false;

for i in `seq 0 ${DAEMON_COUNT}`;
do
    if [ ! -d "data$i" ]; then
            bool=true;
            echo "data$i does not exist. Creating...";
            mkdir data$i
    fi
done

if [ $bool = true ]; then 
    ... 
fi

Is this what you want?

Share:
18,451
Hassan Syed
Author by

Hassan Syed

Member of Technical Staff 2 @ eBay. Interested in Bazel, K8s, ISTIO, gRPC, Golang, Rust, Kotlin, Java and all orchestrations thereof.

Updated on June 22, 2022

Comments

  • Hassan Syed
    Hassan Syed almost 2 years

    I'm a C/C++ programmer and quite stupid in general (or at least the way bash does things it makes me feel confused). I can't wrap my head around process substitution.

    I need to define a global boolean, set it somewhere in a loop, and make use of it in global scope. Could someone please explain in the simplest way possible how to adapt the code below to allow me to achieve my use case, simple enough so that I don't have to contort my brain again tomorrow to try and grasp process substitution .

    # DEFINE HERE
    
    for i in `seq 0 ${DAEMON_COUNT}`;
    do
            if [ ! -d "data$i" ]; then
    # SET HERE
                    echo "data$i does not exist. Creating...";
                    mkdir data$i
            fi
    done
    
    # TEST AND USE HERE
    

    to be honest, I don't think bash is up to the task.... the next block looks like this.

    echo "-------------------------------------------------------------------------------"
    echo "checking the state of potentially running daemons"
    for i in `seq 0 ${DAEMON_COUNT}`;
    do
            if [ ! -e "data$i/mongod.lock" ] ; then
                    echo "[no lock file] mongod process $i does not exist"
            else
                    echo "[lock file exists] process $i lock file exists "
                    I_PID=`cat data$i/mongod.lock`
    
                    if [ ! ${I_PID} ]; then
                            echo "    [GOOD] lock pid empty"
                    elif [ "`ps -p ${I_PID} | grep ${I_PID}`" ]; then
                            echo "    [GOOD] data1 pid: ${I_PID} running"
                    else 
                            echo "[PROBABLY FATAL] data1 pid: ${I_PID} not running."
                    fi 
            fi
    done
    echo "-------------------------------------------------------------------------------"
    

    What I now need is a global array of structs so that I can loop over them and take conditional action to initialize my daemons correctly :/.

    Might just use libc and do this stuff in lua, the only reason I hold back is having to install rocks, I don't like ad-hoc code repositories vomiting whatever they want onto my machine :D