跳转至

Shell数组,字典使用

定义数组

A=(a b c def)

命令 解释 结果
${A[@]} 返回数组全部元素 a b c def
${A[*]} 同上 a b c def
${!A[@]} 返回全部元素下标位置信息 0 1 2 3
${!A[*]} 同上 0 1 2 3
${#A[@]} 返回数组元素总个数 4
${#A[*]} 同上 4
${A[0]} 返回第一个元素 a
${#A[3]} 返回第4个元素def长度,第一个元素从0开始 3
A[3]=xyz 重新设置第4个元素为xyz 此时返回所有元素为a b c xyz
A=("${A[@]}" "val") 数组增加一个元素val 此时返回所有元素为a b c xyz val

遍历数组两种方式

A=(a b c xyz val)

  1. 按序号遍历

    Bash
    for i in "${!A[@]}";do
      printf "%s\t%s\n" "$i" "${A[$i]}"
    done
    
    结果:
    0   a
    1   b
    2   c
    3   xyz
    4   val
    

  2. 按数据遍历

    Bash
    for i in ${A[*]};do
      echo $i
    done
    
    结果:
    a
    b
    c
    xyz
    val
    

定义字典

在 Shell 中,declaretypeset 命令‌在功能上是完全等价的‌,它们都是 Bash 的内建命令,用于声明变量并设置其属性(如整数、只读、数组、环境变量等)

typeset 命令源自 KornShell (ksh),而 declare 是 Bash 引入的更现代的命名,一般都使用declare

但是我在mac电脑上 bash(低版本) 不支持 declare,反而使用 zsh 的 typeset

常用选项

选项 含义 示例
-i name 声明为整数类型,支持直接算术运算 declare -i num=5+3num 值为 8
-a name 声明为普通数组 declare -a colors=("red" "green" "blue")
-A name 声明为关联数组(键为字符串) declare -A cap=([china]=beijing [japan]=tokyo)
-p [name] 显示变量的属性和值 declare -p MY_VAR
-f [name] 列出所有自定义函数 declare -f
-F [name] 仅列出函数名 declare -F
-r name[=value] 声明为只读变量,不可修改或删除 declare -r PI=3.14
-x name[=value] 将变量导出为环境变量 declare -x MY_VAR="value"

-A 定义关联数组并访问

Bash
1
2
3
4
5
6
7
8
9
# 声明变量dic为字典
declare -A dic
# 按照数组方式定义字典变量
dic=([k1]="v1" [k2]="v2" [k3]="v3")

# 也可以挨个定义
dic[k1]="v1"
dic[k2]="v2"
dic[k3]="v3"

具体使用

可以参照数组方式使用

  1. 打印k1的值

echo ${dic["k1"]}

输出: v1

  1. 打印所有value值,使用 * 或者 @ 都可以

echo ${dic[*]}

输出: v1 v3 v2

  1. 打印所有key,使用 * 或者 @ 都可以

echo ${!dic[*]}

输出: k1 k3 k2

  1. 添加一个新元素

dic+=([k4]="v4")

查看: echo ${dic["k4"]}

输出: v4

-r 定义只读变量

Bash
declare -r name="admin"

Shell规定,只读变量生命周期与当前Shell脚本进程相同,且不能消除只读属性和删除只读变量,除非kill当前Shell脚本进程。

-p 显示变量的属性和值

Bash
$declare -p name
declare -r name="admin"

数组,字典结合使用场景

有两个文件:

cat role.ini

Bash
1
2
3
4
es=node1,node2,node3
filebeat=node1,node3,node4,node5
logstash=node1
kibana=node1

cat host.ini

Bash
1
2
3
4
5
6
node1=192.168.1.1
node2=192.168.1.2
node3=192.168.1.3
node4=192.168.1.4
node5=192.168.1.5
node6=192.168.1.6
  • role.ini : 存放应用角色-节点对应关系
  • host.ini : 存放节点-主机列表

需求:

role.inihost.ini 中得到 role - ip 的对应关系

role.ini 表中数据为源

示例脚本如下: vim role-ip.sh

Bash
# 定义三个字典
# role字典;host字典;结果role--ip字典
declare -A IPListDict
declare -A RoleListDict
declare -A RoleIPDict

# 初始化host.ini文件,定义为关联数组 IPListDict
# IPListDict='([node1]="192.168.1.1" [node2]="192.168.1.2" ...)'
function initIPListDict(){
    file=$1
    # 使用循环定义字典
    while read line;do 
    IPListDict["${line%%=*}"]="${line##*=}"
    done < $file
}
# 初始化role.ini文件,定义为关联数组 RoleListDict
# RoleListDict='([es]="node1,node2,node3" [logstash]="node1" ... )'
function initRoleListDict(){
    file=$1
    # 使用循环定义字典
    while read line;do 
    RoleListDict["${line%%=*}"]="${line##*=}"
    done < $file
}

#对于role.ini字典的key循环处理,切割value,作为host.ini中key,得到value并保存
# 遍历角色对应关系的key
function initRoleIPDict(){
   for key in $(echo ${!RoleListDict[*]})
   do
      OLD_IFS="$IFS"
      IFS=","
      # arr拿到role的key, es logstash ...
      arr=(${RoleListDict[$key]})
      IFS="$OLD_IFS"
      roleallip=""
      # 遍历role key
      for element in ${arr[@]}   
        do
          # element 的值,es=node1...
          element=${IPListDict[$element]}
          roleallip=$roleallip$element","
        done
      # 删除最后一个逗号“,”
      roleallip=${roleallip%?}
      RoleIPDict[$key]=$roleallip
  done
 }

hostfile="/root/host.ini"
rolefile="/root/role.ini"
initIPListDict ${hostfile}
initRoleListDict ${rolefile}
initRoleIPDict

# 打印Role-IP结果
for k in "${!RoleIPDict[@]}"
do
   echo $k"="${RoleIPDict[$k]}
done

脚本输出对应关系:

Bash
1
2
3
4
es=192.168.1.1,192.168.1.2,192.168.1.3
logstash=192.168.1.1
kibana=192.168.1.1
filebeat=192.168.1.1,192.168.1.3,192.168.1.4,192.168.1.5

脚本中的一些说明

关于字符串的截取,参考Shell脚本字符串截取

比如用到的:

Bash
1
2
3
4
5
6
# 删除等号之后的所有值
${line%%=*}
# 删除等号之前的所有值
${line##*=}
# 删除最后一个字符,这里是逗号','
${roleallip%?}

按照逗号切割字符串,将逗号转为空格,因为shell定界符默认是空格(IFS保存的是shell 默认定界符)

Bash
1
2
3
4
5
6
7
8
# 先保存默认的定界符为OLD_IFS
OLD_IFS="$IFS"
# 重新定义逗号','为新的定界符
IFS=","
# 开始处理需要的数组,将有逗号的值变为了空格,方便循环
arr=(${RoleListDict[$key]})
# 最后处理结束还需要还原默认定界符
IFS="$OLD_IFS"

其实也可以使用字符替换的方式处理

Bash
1
2
3
4
5
6
# node1,node2,node3
arr=(${RoleListDict[$key]})

# 将逗号','换成空格' ',注意大括号中最后有空格
echo ${arr//,/ }
输出: `node1 node2 node3`