背景
上回说到:
Shell快速判断服务器是否在线以及占用情况(进程是否运行)
这次进一步:
希望对在线空闲的服务器执行一些操作。
尝试
容易想到构建一个数组保存空闲节点名称,随后遍历该数组发送指令。
代码如下:
#!/bin/bash
echo Checking hosts...
val=()
for $host in Server-{{0..9},{A..Z}}
do
{
if ping -w 3 $host > /dev/null
then
if [ `ssh user@$host pgrep -c myapp` -eq 0 ]
then
# The server is free
val+=($host)
fi
fi
}&
done
wait
for host in ${val[*]}
do
ssh user@$host bash ~/work.sh
done
问题
上述代码看似没毛病,可运行没反应。
通过添加echo ${val[*]}
打印变量发现数组val值为空!
删去节点检测的&和wait改为顺序执行后val可以正常赋值?
思考
顺序检测36台服务器效率过低,方案否决!
探究为何后台执行变量无法追加到val数组?
难道shell变量也存在作用域?分全局和局部?
查阅资料得:
在 Shell 中定义的变量,默认就是全局变量。
所谓全局变量,就是指变量在当前的整个 Shell 进程中都有效。
每个 Shell 进程都有自己的作用域,彼此之间互不影响。
比如,开启两个终端同时访问变量$a的值可以是不同的。
而for do {}& done wait
结构下,后台产生了多个相互独立的进程同时执行,
加速同时也隔离彼此变量传递,导致主进程变量val无法被赋值。
解决
既然如此,只需要聚合多进程的结果一起传入后续函数即可实现变量跨进程传递。
这里将节点判断部分封装为一个函数,空闲节点名称直接打印到标准输出。
再将函数输出作为输入构建循环列表,最终满足要求。
修改后代码如下:
#!/bin/bash
echo Checking hosts...
check(){
for $host in Server-{{0..9},{A..Z}}
do
{
if ping -w 3 $host > /dev/null
then
if [ `ssh user@$host pgrep -c myapp` -eq 0 ]
then
# The server is free
echo $host
fi
fi
}&
done
wait
}
for host in `check`
do
ssh user@$host bash ~/work.sh
done