bash background process modify global variable bash background process modify global variable bash bash

bash background process modify global variable


Upgrade 2019

Playing with bash_ipc_demo adding completion and a graph generator.

Rendez-vous

If you wanna have two independant process which could communicate, you have to place a rendez-vous somewhere both process can reach.

This could be a simple file, a fifo pipe, a unix socket, a TCP socket or maybe else (Rexx port).

and other

Bash don't have a equivalent to rexx port, so there is a little sample, using a rendez-vous file, that work (on my Linux).

I'm using shared memory /dev/shm, to reduce disk load.

Simple counter sample

$ back_func() {    while :;do        echo $(($(</dev/shm/foo)+1)) >/dev/shm/foo;        sleep .3;      done;}

Let play

$ echo 1 >/dev/shm/foo$ back_func &$ echo $(</dev/shm/foo)4$ echo $(</dev/shm/foo)21

Than stop now:

$ fgback_func^C

or

$ kill $!$[1]+  Terminated              back_func

More than one variables

For having many vars, there could by a nice manner:

$ back_func() {    declare -A MYGLOBAL    local vars    while :; do        ((MYGLOBAL["counter"]++))        IFS=\ / read -a vars <<< "$(</proc/uptime) $(</proc/loadavg)"        MYGLOBAL["uptime"]=$vars        MYGLOBAL["idle"]=${vars[1]}        MYGLOBAL["l01m"]=${vars[2]}        MYGLOBAL["l05m"]=${vars[3]}        MYGLOBAL["l15m"]=${vars[4]}        MYGLOBAL["active"]=${vars[5]}        MYGLOBAL["procs"]=${vars[6]}        MYGLOBAL["lpid"]=${vars[7]}        MYGLOBAL["rand"]=$RANDOM        MYGLOBAL["crt"]=$SECONDS        declare -p MYGLOBAL > /dev/shm/foo        sleep 1    done}

Then

$ back_func &[1] 27429$ . /dev/shm/foo$ echo ${MYGLOBAL['counter']}5$ echo ${MYGLOBAL['lpid']}27432

and from there, why not:

$ dumpMyGlobal() {    . /dev/shm/foo    printf "%8s " ${!MYGLOBAL[@]}    echo    printf "%8s " ${MYGLOBAL[@]}    echo}$ dumpMyGlobal    l15m   uptime      crt    procs     lpid   active     rand     idle     l05m  counter     l01m     0.42 13815568.06       95      554      649        1    31135 21437004.95     0.38       73     0.50 $ dumpMyGlobal    l15m   uptime      crt    procs     lpid   active     rand     idle     l05m  counter     l01m     0.41 13815593.29      120      553      727        2     3849 21437046.41     0.35       98     0.33 

or

$ dumpMyGlobal() {    . /dev/shm/foo    sort <(        paste <(            printf "%-12s\n" ${!MYGLOBAL[@]}          ) <(printf "%s\n" ${MYGLOBAL[@]})    )}$ dumpMyGlobalactive              1counter             297crt                 337idle                21435798.86l01m                0.40l05m                0.44l15m                0.45lpid                30418procs               553rand                7328uptime              13814820.80

Get variable with snapshot

and finally getMyGlobalVar function

$ declare -A MYGLOBALLOCK   # snapshot variable$ getMyGlobalVar () {     local i sync=false    [ "$1" == "--sync" ] && shift && sync=true    if [ -z "${MYGLOBALLOCK[*]}" ] || $sync; then        . /dev/shm/foo        for i in ${!MYGLOBAL[@]}        do            MYGLOBALLOCK[$i]=${MYGLOBAL[$i]}        done    fi    echo ${MYGLOBALLOCK[$1]}}

will require --sync flag for re-reading rendez-vous in order to let you look about each fields from the same snapshot.

$ getMyGlobalVar --sync idle362084.12$ getMyGlobalVar idle362084.12$ getMyGlobalVar rand1533$ getMyGlobalVar rand1533$ getMyGlobalVar --sync rand43256$ getMyGlobalVar idle362127.63

Full useable demo:

There is a full sample: bash_ipc_demo or bash_ipc_demo.shz

You could use by:

wget http://f-hauri.ch/vrac/bash_ipc_demosource bash_ipc_demoback_func helpUsage: back_func [-q] [start [-g N]|stop|restart|status|get|dump|help]   -q    Quiet   -g N  Start daemon, setting uptime_useGraph to N valuesback_func statusBackground loop function is not running.back_func start -g 3600back_func statusBackground loop function (19939) is running.

From there, if you source bash_ipc_demo in another terminal, you could do the list into them.

You could even close the first terminal.

back_func dumpbackFunc_count                     13backFunc_now      2016-04-06 17:03:19backFunc_pid                    19939backFunc_running                  yesbackFunc_start    2016-04-06 17:03:07cpu_numcores                        2loadavg_15min                    0.44loadavg_1min                     0.66loadavg_5min                     0.54loadavg_active                      1loadavg_last_pid                20005loadavg_process                   650random                        3714432uptime_graph_val                 3600uptime_idle                 425499.43uptime_up                   495423.53uptime_usage1sec                 9.90uptime_usage                    57.06uptime_useGraph  57.06 8.91 7.50 6.93 12.00 9.41 7.84 9.90 7.50 11.88 7.92 9.31 9.90 

Then, you could get one value

back_func get backFunc_pid newVarecho $newVar 19939

or build a quick cpu graph:

lastMinuteGraph -p -o /tmp/lastMinuteGraph.png -W 640 -H 220

This will render a 640x220 PNG graphic, with uptime_graph_val values.In this case, as back_func start was invoked with -g 3600 from morethan one hour, graphic show 3600 peek on 640 columns and 0-100% on 220 lines:

LastHourGraph

(Nota: Command was originaly named lastMinuteGraph as 1st version of this just stored 60 values, now this use uptime_graph_val for number of values to store. As I've used -g 3600 argument, this command could by named lastHourGraph).

Then:

back_func stop  back_func get backFunc_end2019-01-02 16:35:00


According to the Bash manual here,

If a command is terminated by the control operator ‘&’, the shell executes the command asynchronously in a subshell.

And since a process run in a subshell cannot modify the environment of the parent shell, I guess what you are trying to do is only possible via temp files / named pipes. Or you could rethink your approach.


If the main process (let's call it main.sh) is another periodically running bash script then you could simply have the the other script (let's call it other.sh) write the value to a file (let's call this file value.sh).

other.sh

#! /bin/bash  echo "SOME_VAR=42" > /tmp/value.sh

main.sh

#! /bin/bash  . /tmp/value.sh  # Now you can use SOME_VAR