八.Shell中的数组
一、数组分类
数组:用一个变量存储一组数据,并能够对这组数据中的某一个数据单独操作。
普通数组:只能使用整数作为数组索引(元素的下标)
关联数组:可以使用字符串作为数组索引(元素的下标)
数组是Shell的一种特殊变量,是一组数据的集合,里面的每个数据被称为一个数组元素。
当前Bash仅支持一维索引数组和关联数组,Bash对数组的大小没有限制。
Shell中数据类型不多,比如说字符串,数字类型,数组。数组是其中比较重要的一种,其重要应用场景,可以求数组长度,元素长度,遍历其元素,元素切片,替换,删除等操作,使用非常方便。
Shell中的数组不像JAVA/C,只能是一维数组,没有二维数组;数组元素大小无约束,也无需先定义数组的元素个数;但其索引则像JAVA/C/Python,从0开始,下面其常用的方式进行总结.
注意:
普通数组
索引号为数字,默认起始索引号为0
不用声明,直接使用
关联数组
索引格式可以自定义,关联数组数据实旨为key、Value这样的键值构成的集合
必须先声明
二、普通数组
普通数组定义:用括号来表示数组,数组元素(变量)用空格符号分割开。
备注:
1) 数组中的元素,必须以"空格"来隔开,这是其基本要求;
2) 定义数组其索引,可以不按顺序来定义,比如说:names=([0]=Jerry [1]=Alice [2]=David [8]=Wendy);
3)字符串是SHELL中最重要的数据类型,其也可通过($str)来转成数组,操作起来非常方便;
定义数组的一般形式为:
方法1:一次赋予一个值
语法:数组名[索引下标]=值
array=() #定义一个空数组 array1[0]=a array1[1]=b array1[2]=c
这会得到一个包含三个元素的数组array1
方法2:一次赋予多个值
语法: 数组名=(值1 值2 值3 ...)
array=(var1 var2 var3 var4)
array1=(`cat /etc/passwd`) 将文件中每一行赋值给array1数组
array2=(`ls /root`)
array3=(harry amy jack "Miss Hou")
array4=(1 2 3 4 "hello world" [10]=linux)
元素之间以空格隔开
如图:
只赋值指定元素
交互式数组
案例演示:
# 一次赋予多个值 array1=(a b c d e f g) # 分别赋值 array2[0]=1 array2[1]=2 array2[10]=11 # 命令的执行结果赋值 array3=(`cat /etc/passwd`) # 个性化diy赋值 array4=(1 2 3 "hello world" [8]="haha")
三、关联数组
declare:强制设置类型属性
语法:declare 选项 变量名
选项 注释
-i 强制为整数
-a 定义为数组
-x 设置为全局变量/环境变量
-r 设置为只读
-i:定义变量为数值类型
[root@localhost ~]# declare -i x=20
[root@localhost ~]# declare -i y=3
[root@localhost ~]# declare -i c=$x*$y
[root@localhost ~]# echo $c
60
-a:定义变量为数组类型
[root@localhost ~]# declare -a tea
[root@localhost ~]# declare -a | grep tea
declare -a tea='()'
[root@localhost ~]# tea=( plj hpg zhf zzg)
[root@localhost ~]# declare -a | grep tea
declare -a tea='([0]="plj" [1]="hpg" [2]="zhf" [3]="zzg")'
定义一个名为user_info的数组
declare -A user_info user_info[name]=devops user_info[age]=18 或者 user_info=([name]=devops [age]=18)
注意:在函数外部定义的关联数组为全局变量,在函数内部定义的关联数组为局部变量
1.定义管理数组
关联数组使用首先需要申明该数组为关联数组,申明方式: declare -A 数组名称
# 声明关联数组 [root@shell ~]# declare -A asso_array1 [root@shell ~]# declare -A asso_array2
2.关联数组赋值
一次赋一个值
# 数组名[索引名称]=变量值 [root@shell ~]# asso_array1[linux]=one [root@shell ~]# asso_array1[java]=two [root@shell ~]# asso_array1[php]=three
一次附多个值
asso_array2=([name1]=harry [name2]=jack [name3]=amy [name4]="Miss zhang")
查看关联数组
[root@shell ~]# declare -A declare -A asso_array1='([php]="three" [java]="two" [linux]="one" )' declare -A asso_array2='([name3]="amy" [name2]="jack" [name1]="harry" [name4]="Miss zhang" )'
3 管理数组取值
# 指定索引取值 [root@shell ~]# echo ${asso_array1[linux]} one [root@shell ~]# echo ${asso_array1[php]} three # 去除数组所有值 [root@shell ~]# echo ${asso_array1[*]} three two one [root@shell ~]# echo ${!asso_array1[*]} php java linux # 统计数组元素个数 [root@shell ~]# echo ${#asso_array1[*]} 3 [root@shell ~]# echo ${#asso_array2[*]} 4 # 取出数组所有元素的索引 [root@shell ~]# echo ${!asso_array2[*]} name3 name2 name1 name4
四、处理数组
取值方式: ${数组名称[索引]}
索引: 默认情况下索引是指数组中的元素[存的值]在数组中的顺序,从0开始计数,关联数组除外。
比如:
array=(var1 var2 var3 var4)
array数组中存有4个元素,分别是:var1 var2 var3 var4
那么我想取出var2这个元素,那么就得先看看他在数组中的位置,数组中的元素索引如下:
元素 var1 var2 var3 var4 索引 0 1 2 3
所以正确表示array数组中元素var2的方式是:${array[1]}
数组取值的多种方式:
${array[i]} i表示元素的索引 使用 @ 或 * 可以获取数组中的所有元素: echo ${array[0]} 获取第一个元素 echo ${array[*]} 获取数组里的所有元素 echo ${#array[*]} 获取数组里所有元素个数 echo ${!array[@]} 获取数组元素的索引 echo ${array[@]:1:2} 获取指定的元素范围。1代表从索引为1的元素开始获取;2代表获取1索引及其后面的元素总共2个元素
案例演示
[root@shell ~]# vi array_test.sh #!/bin/bash # 一次赋予多个值 array1=(a b c d e f g) # 分别赋值 array2[0]=1 array2[1]=2 array2[10]=11 # 命令的执行结果赋值 array3=(`cat /etc/passwd`) # 个性化diy赋值 array4=(1 2 3 "hello world" [8]="haha") # 取值 ${数组名[索引]} echo ${array1[2]} echo ${array2[1]} echo ${array3[0]} echo ${array4[8]} # 获取array2中的所有元素 echo ${array2[*]} # 获取array2中所有元素个数 echo ${#array2[*]} # 获取array2数组元素的索引 echo ${!array2[@]} # 获取指定的元素范围 echo ${array1[*]:2:3} [root@shell ~]# sh array_test.sh c 2 root:x:0:0:root:/root:/bin/bash haha 1 2 11 3 0 1 10 c d e
数组数据处理
语法:${ARRAY[@]:offset:number}
记忆思路:
偏移量:从左到右元素字符串起始偏移量编号为0;从右到从元素字符串起始偏移量编号为-1
offset:元素字符串偏移量编号 对应Python字符串切片中的起始位置
offset为正数时代表从左向右截取,为负数时代表从右向左截取。
number:要截取元素字符串的长度 对应Python字符串切片中的结束位置。
number为正数时代表截取长度,为负数则代表结束位置,当number为代表结束位置时截取内容不包括第m位
注意:使用负数截取时,负数前必须用空格与:冒号隔开。
实例1:offset为正数时,从左向右截取:${ARRAY[@]:n:m},n为起终位置,m为截取长度
从左向右,截取起始位置为n,取m个字符。
如果不指定截取长度m时,则是截取第n位到元素字符串的最后
实例2:
offset为负数时,从右向左截取,为负数时:冒号后必须加空格${ARRAY[@]: -n: -m} 或${ARRAY[*]: -n:m}
${ARRAY[@]: -n:m} :从右到左,元素字符串偏移量由右到左编号为-1到-n,截取起始位置为-n,取m个字符。
${ARRAY[@]: -n: -m} :从右到左,元素字符串偏移量由右到左编号为-1到-n,截取起始位置为-n,结束位置为-m个字符。
一定要铭记数组一旦被定义,只能向数组中一个一个的追加元素,或修改元素值,不能批量添加元素。批量赋值批量的是数组初始化。
ARRAY[${#ARRAY[]}]=value 元素个数加1来赋值
1.直接获取元素的值及数组的信息
1、获取单个元素的值
echo ${array1[0]} #获取第一个元素的值 echo ${array1[-1]} #获取最后一个元素的值
如图:
2、获取所有元素的值
echo ${array1[*]} 或者用echo ${array1[@]}
如图:
3、统计数组的长度
echo ${#array1[*]}
如图:
4、打印数组的下标值
echo ${!array1[@]}
echo ${!user_info[@]}
如图:
5、删除数组元素和数组
unset array2[2] #删除索引数组的第三个元素 unset user_info[age] #删除关联数组中索引为age的元素 unset array2 #删除数组
2.使用循环的方式遍历数组
方法一:直接取值
for i in "${array1[@]}" do echo $i done
方法二:通过下标取值
for i in ${!array1[@]} do echo ${array1[i]} done
方法三:C语言风格
for((i=0;i<${#array1[@]};i++)) do echo ${array1[i]} done
五、$*和$@的区别
1.当直接通过echo获取数组所有元素时,它们是一样的效果
而在循环中它们就有区别了
总结:${array1[*]}会将数组元素视为一个整体,而${array1[@]}将所有数组元素视为独立的个体,推荐使用${array1[@]}
六、技巧
字符串以冒号分隔存入数组
string="12:34:56" array=(${string/:/ })
七、综合案例
写一个监控CPU 平均负载值的脚本
案例需求
分别打印 CPU 1min 5min 15min load 负载值
案例思路
如何取CPU负载值,有哪些命令
如果存储在内存中,变量、数组
案例步骤
收集cpu load 平均负载值到数组
打印输出对应的负载值
代码实现
[root@shell ~]# cat cup_load.sh #!/bin/bash #1、收集负载值 cpu_load=(`uptime|tr -s " "|cut -d " " -f9-11|tr "," " "`) #2、输出负载值 echo "CPU 1 min 平均负载为: ${cpu_load[0]}" echo "CPU 5 min 平均负载为: ${cpu_load[1]}" echo "CPU 15 min 平均负载为: ${cpu_load[2]}"
八、案例展示
【数组长度】
备注:
1) 使用${array_name[@]} 或者 ${array_name[*]} 都可以全部显示数组中的元素
2) 同样道理${#array_name[@]} 或者 ${#array_name[*]}都可以用来求数组的长度
3)求数组中元素的长度方法有很多,相当于求字符串的长度
【数组索引】
[root@locathost ~]# s="A,B,C,D"
[root@locathost ~]# a=(`echo $s | tr ',' ' '`) # 将字符串转变成数组
[root@locathost ~]# echo ${!a[@]} # 求数组中的索引
0 1 2 3
【元素删除】
[root@localhost ~]# a=(A B C D)
[root@localhost ~]# unset a[2] # 删除索引为2的元素
[root@localhost ~]# echo ${a[@]} # 显示删除后的元素
A B D
【数组遍历】
脚本输出:
备注:
1) 可以使用标准的for循环,这种类C语言的方式来遍历数组中的元素
2) for 元素 in 元素集(数组) 这种类Python的方式来遍历数组
3)从代码可读性与执行速度来看,推荐使用第二种方式
【数组赋值】
备注:
1) 第一种是给已经存在的元素项重新赋值
2) 当然也可以给不存在的索引添加赋值,可以看下面的示例
【数组添加】
【数组切片】
数组切片
元素切片
备注:
1) 通用的格式${array[@]:起始位置:长度},中间以":"隔开,如果第二项省略的话,就取后面所有的项
2) 切片后返回的是字符串,可以通过 新数组=(${旧数组[@]:索引:长度})来索引,参见上面最后一个例子
3) 区别于Python之一:起始位置可以为负数,但必须以放在()中,长度不能为负数
4)区别于Python之二:第二项在Python里面是结束索引,在Shell则代表所取元素的长度
5) 区别于Python之三:Python可以通过 list[-1:-4:-2]来反向取数,在Shell则实现不了
【数组替换】
${array[@]/x/y} 最小匹配替换,每个元素只替换一次
${array[@]//x/y} 最大匹配替换,每个元素可替换多次
${array[@]/x/} 最小匹配删除,只删除一个符合规定的元素
${array[@]//x/} 最大匹配删除,可删除多个符合规定的元素
${array[@]/#x/y} 从左往右匹配替换,只替换每个元素最左边的字符
${array[@]/%x/y} 从右往左匹配替换,只替换每个元素最右边的字符
【数组删除】
# 每个元素,从左向右进行最短匹配
## 每个元素,从左向右进行最长匹配
% 每个元素,从右向左进行最短匹配
%% 每个元素,从右向左进行最长匹配
九、数组应用
示例一: 将ifconfig命令取到的本地IP: 127.0.0.1逐行显示出来
脚本输出:
示例二: 模拟堆栈的push,pop,shift,unshift操作
脚本输出:
示例三: 在1-10间,随机生成10个不重复的数,将其放置于数组中
脚本输出:
备注:
1) 生成[1,10]范围内不重复的随机整数,并保存到数组array中
2) seq 1 10 用于生成1~10的整数序列(包含边界值1和10)
3) awk中的rand()函数用于随机产生一个0到1之间的小数值(保留小数点后6位)
4)rand()只生成一次随机数,要使用srand()函数使随机数滚动生成
5) 括号里留空即默认采用当前时间作为随机计数器的种子,这样以秒为间隔,随机数就能滚动随机生成了
6) 由于以秒为间隔,所以如果快速连续运行两次脚本(1s内),你会发现生成的随机数还是一样的
示例四: 将字符串处理后转为为数组,再对其打印输出
脚本输出:
示例五: 用read -a参数,从标准输入中读取数组,再做操作
脚本输出:
示例六: 判断某个变量,是否在数组中,在输出YES,否输出NO
脚本输出:
示例七: 对数组中的元素进行排序
示例八: 将/etc/passwd文件中以:分隔的第一列,即用户名放置于一个数组中
示例九: 将1-8,每个数自乘后输出
脚本输出:
示例十: 借助数组来设置SHELLS的环境变量
示例十一: 设置IFS,读取文件内容示例
示例十二: 利用eval,模拟实现数组的功能
脚本输出:
示例十三: 利用数组来实现冒泡排序
思路:会重复地走访过要排序的数组,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。越大的元素会经由交换慢慢“浮”到数列的顶端
脚本输出:
示例十四: 利用数组来求最大值
脚本输出
参考:
https://blog.csdn.net/anqixiang/article/details/114415491
https://www.jb51.net/article/208979.htm