bash background process modify global variable

后端 未结 3 426
陌清茗
陌清茗 2020-12-03 06:45

I have a global var foo=\"some value\" and a background process back_func, I want to the background process to access $foo and modify its value, which can be seen by the mai

相关标签:
3条回答
  • 2020-12-03 07:19

    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).

    bash and other shell

    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:

    $ fg
    back_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[@]})
        )
    }
    
    $ dumpMyGlobal
    active              1
    counter             297
    crt                 337
    idle                21435798.86
    l01m                0.40
    l05m                0.44
    l15m                0.45
    lpid                30418
    procs               553
    rand                7328
    uptime              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 idle
    362084.12
    
    $ getMyGlobalVar idle
    362084.12
    
    $ getMyGlobalVar rand
    1533
    
    $ getMyGlobalVar rand
    1533
    
    $ getMyGlobalVar --sync rand
    43256
    
    $ getMyGlobalVar idle
    362127.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_demo
    
    source bash_ipc_demo
    back_func help
    Usage: back_func [-q] [start [-g N]|stop|restart|status|get|dump|help]
       -q    Quiet
       -g N  Start daemon, setting uptime_useGraph to N values
    
    back_func status
    Background loop function is not running.
    
    back_func start -g 3600
    
    back_func status
    Background 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 dump
    backFunc_count                     13
    backFunc_now      2016-04-06 17:03:19
    backFunc_pid                    19939
    backFunc_running                  yes
    backFunc_start    2016-04-06 17:03:07
    cpu_numcores                        2
    loadavg_15min                    0.44
    loadavg_1min                     0.66
    loadavg_5min                     0.54
    loadavg_active                      1
    loadavg_last_pid                20005
    loadavg_process                   650
    random                        3714432
    uptime_graph_val                 3600
    uptime_idle                 425499.43
    uptime_up                   495423.53
    uptime_usage1sec                 9.90
    uptime_usage                    57.06
    uptime_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 newVar
    echo $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 more than one hour, graphic show 3600 peek on 640 columns and 0-100% on 220 lines:

    (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_end
    2019-01-02 16:35:00
    
    0 讨论(0)
  • 2020-12-03 07:27

    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
    
    0 讨论(0)
  • 2020-12-03 07:34

    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.

    0 讨论(0)
提交回复
热议问题