Technic1an
Technic1an

Reputation: 2797

How to increment a global variable within another bash script

Question,

I want to have a bash script that will have a global variable that can be incremented from other bash scripts.

Example: I have a script like the following:

 #! /bin/bash
export Counter=0
   for SCRIPT in /Users/<user>/Desktop/*sh
    do
        $SCRIPT
    done
echo $Counter

That script will call all the other bash scripts in a folder and those scripts will have something like the following:

if [ "$Output" = "$Check" ]
    then
       echo "OK"
      ((Counter++))

I want it to then increment the $Counter variable if it does equal "OK" and then pass that value back to the initial batch script so I can keep that counter number and have a total at the end.

Any idea on how to go about doing that?

Upvotes: 2

Views: 2604

Answers (3)

Charles Duffy
Charles Duffy

Reputation: 295288

Environment variables propagate in one direction only -- from parent to child. Thus, a child process cannot change the value of an environment variable set in their parent.

What you can do is use the filesystem:

export counter_file=$(mktemp "$HOME/.counter.XXXXXX")
for script in ~user/Desktop/*sh; do "$script"; done

...and, in the individual script:

counter_curr=$(< "$counter_file" )
(( ++counter_curr ))
printf '%s\n' "$counter_curr" >"$counter_file"

This isn't currently concurrency-safe, but your parent script as currently written will never call more than one child at a time.


An even easier approach, assuming that the value you're tracking remains relatively small, is to use the file's size as a proxy for the counter's value. To do this, incrementing the counter is as simple as this:

printf '\n' >>"$counter_file"

...and checking its value in O(1) time -- without needing to open the file and read its content -- is as simple as checking the file's size; with GNU stat:

counter=$(stat -f %z "$counter_file")

Note that locking may be required for this to be concurrency-safe if using a filesystem such as NFS which does not correctly implement O_APPEND; see Norman Gray's answer (to which this owes inspiration) for a working implementation.

Upvotes: 7

Norman Gray
Norman Gray

Reputation: 12514

As described, you can't do this, since there isn't anything which corresponds to a ‘global variable’ for shell scripts.

As the comment suggests, you'll have to use the filesystem to communicate between scripts.

One simple/crude way of doing what you describe would be to simply have each cooperating script append a line to a file, and the ‘global count’ is the size of this file:

#! /bin/sh -
echo ping >>/tmp/scriptcountfile

then wc -l /tmp/scriptcountfile is the number of times that's happened. Of course, there's a potential race condition there, so something like the following would sequence those accesses:

#! /bin/sh -
(
flock -n 9 
echo 'do stuff...'
echo ping >>/tmp/stampfile
) 9>/tmp/lockfile

(the flock command is available on Linux, but isn't portable).

Of course, then you can start to do fancier things by having scripts send stuff through pipes and sockets, but that's going somewhat over the top.

Upvotes: 1

J&#246;rn Hees
J&#246;rn Hees

Reputation: 3428

You could source the other scripts, which means they're not running in a sub-process but "inline" in the calling script like this:

#! /bin/bash
export counter=0
   for script in /Users/<user>/Desktop/*sh
   do
       source "$script"
   done
echo $counter

But as pointed out in the comments i'd only advise to use this approach if you control the called scripts yourself. If they for example exit or have variables clashing with each other, bad things could happen.

Upvotes: 2

Related Questions