【问题标题】:bash background process modify global variablebash后台进程修改全局变量
【发布时间】:2012-10-23 19:46:37
【问题描述】:

我有一个全局的var foo="some value"和一个后台进程back_func,我想让后台进程访问$foo并修改它的值,这可以被主进程看到。它类似于以下内容:

#!/bin/bash
foo=0

function back_func {
     foo=$(($foo+1))
     echo "back $foo"
}

(back_func) &
echo "global $foo"

上面脚本的结果是

global 0
back 1

怎么才能得到global和back都是'1'的结果呢?即后台进程的修改可以返回到主进程。

【问题讨论】:

  • “主进程”是另一个 bash 脚本吗?它会定期重启吗?
  • 使用环境变量的动机是什么?恕我直言,信息的传输可以通过一个更易于共享的文件来完成。
  • @ct_ 是的,主进程是另一个定期运行的 bash 脚本。
  • @WolfgangFahl 实际上有很多这样的var,如果我们为每个var创建一个文件来共享它的值,那么管理整个程序会更加复杂。但是对于小型程序,通过文件共享值是一个好主意。
  • @algosolo 好的。如果主进程(我们称之为 main.sh)是另一个定期运行的 bash 脚本,那么您可以简单地让另一个脚本(我们称之为 other.sh)编写文件的值(我们称这个文件为 value.sh)。 **other.sh**#! /bin/bashecho "SOME_VAR=42" > /tmp/value.sh**main.sh**#! /bin/bash. /tmp/other.sh# Now SOME_VAR will be set

标签: bash variables background subprocess global


【解决方案1】:

2019 年升级

使用bash_ipc_demo 添加完成和图形生成器。

会合

如果你想有两个可以通信的独立进程,你必须在两个进程都可以到达的地方放置一个 rendez-vous

这可能是一个简单的文件、fifo 管道、unix 套接字、TCP 套接字或其他(Rexx 端口)。

和其他

Bash 没有与 rexx 端口等效的端口,因此有一个使用会合文件的小示例(在我的 Linux 上)。

我正在使用 共享内存 /dev/shm,以减少磁盘负载。

简单的计数器示例

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

让我们玩

$ echo 1 >/dev/shm/foo
$ back_func &

$ echo $(</dev/shm/foo)
4

$ echo $(</dev/shm/foo)
21

比现在停止:

$ fg
back_func
^C

$ kill $!
$
[1]+  Terminated              back_func

多个变量

对于有很多变量,可以通过一种很好的方式:

$ 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
}

然后

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

从那里开始,为什么不呢:

$ 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 

$ 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

通过快照获取变量

最后是getMyGlobalVar函数

$ 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]}
}

需要--sync 标志来重新读取rendez-vous,以便让您从同一个快照查看每个字段。

$ getMyGlobalVar --sync idle
362084.12

$ getMyGlobalVar idle
362084.12

$ getMyGlobalVar rand
1533

$ getMyGlobalVar rand
1533

$ getMyGlobalVar --sync rand
43256

$ getMyGlobalVar idle
362127.63

完整可用的演示:

有一个完整的样本:bash_ipc_demobash_ipc_demo.shz

你可以使用:

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.

从那里,如果您在另一个终端上source bash_ipc_demo,您可以将列表放入其中。

你甚至可以关闭第一个终端。

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 

那么,你可以得到一个值

back_func get backFunc_pid newVar
echo $newVar 
19939

或者构建一个快速cpu图:

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

这将呈现一个 640x220 PNG 图形,具有uptime_graph_val 值。 在这种情况下,back_func start-g 3600 调用,来自更多 超过一小时,图形显示 640 列上 3600 次窥视和 220 行上 0-100%:

(注意: 命令最初被命名为 lastMinuteGraph,因为它的第一个版本只存储了 60 个值,现在这个使用 uptime_graph_val 来存储值的数量。因为我使用了-g 3600 参数,所以这个命令可以命名为lastHourGraph)。

然后:

back_func stop  
back_func get backFunc_end
2019-01-02 16:35:00

【讨论】:

【解决方案2】:

如果主进程(我们称之为 main.sh)是另一个定期运行的 bash 脚本,那么您可以简单地让另一个脚本(我们称之为 other.sh)将值写入文件(我们称之为文件值.sh)。

other.sh

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

ma​​in.sh

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

【讨论】:

  • 不加锁,会有一些风险:&gt;/tmp/value.sh 截断输出文件 before echo 写入它,所以有一个时间窗口,它是空的,然后部分写入。如果你想要一个确定的东西,你应该在获取文件之前使用flock 获取一个读锁,并在修改它之前获取一个写锁。
【解决方案3】:

根据 Bash 手册here

如果命令被控制运算符“&”终止,shell 会在子 shell 中异步执行命令。

而且由于在子 shell 中运行的进程无法修改父 shell 的环境,我猜你试图做的事情只能通过临时文件/命名管道来实现。或者你可以重新考虑你的方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多