八.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

 

posted @ 2018-05-03 13:06  钟桂耀  阅读(290)  评论(0编辑  收藏  举报