bash后台进程修改全局变量
Posted
技术标签:
【中文标题】bash后台进程修改全局变量【英文标题】:bash background process modify global variable 【发布时间】: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/bash
echo "SOME_VAR=42" > /tmp/value.sh
**main.sh**
#! /bin/bash
. /tmp/other.sh
# Now SOME_VAR will be set
【参考方案1】:
2019 年升级
使用bash_ipc_demo
添加完成和图形生成器。
会合
如果你想有两个可以通信的独立进程,你必须在两个进程都可以到达的地方放置一个 rendez-vous。
这可能是一个简单的文件、fifo 管道、unix 套接字、TCP 套接字或其他(Rexx 端口)。
bash 和其他shell
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_demo 或 bash_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
【讨论】:
为了好玩,有一个compressed bash source file【参考方案2】:如果主进程(我们称之为 main.sh)是另一个定期运行的 bash 脚本,那么您可以简单地让另一个脚本(我们称之为 other.sh)将值写入文件(我们称之为文件值.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
【讨论】:
不加锁,会有一些风险:>/tmp/value.sh
截断输出文件 before echo
写入它,所以有一个时间窗口,它是空的,然后部分写入。如果你想要一个确定的东西,你应该在获取文件之前使用flock
获取一个读锁,并在修改它之前获取一个写锁。【参考方案3】:
根据 Bash 手册here,
如果命令被控制运算符“&”终止,shell 会在子 shell 中异步执行命令。
而且由于在子 shell 中运行的进程无法修改父 shell 的环境,我猜你试图做的事情只能通过临时文件/命名管道来实现。或者你可以重新考虑你的方法。
【讨论】:
以上是关于bash后台进程修改全局变量的主要内容,如果未能解决你的问题,请参考以下文章