shell编程之数组

147 阅读5分钟

1 什么是数组

数组(Array)是有序的元素序列。若将有限个类型相同的变量的集合命名,那么这个名称为数组名。

组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量。

用于区分数组的各个元素的数字编号称为下标。元素的下标从0开始。

数组是在程序设计中,为了处理方便, 把具有相同类型的若干元素按有序的形式组织起来的一种形式。 这些有序排列的同类数据元素的集合称为数组。

2 定义数组的方法

Shell中,数组用括号来表示,元素用"空格"符号分隔开。

数组包括的数据类型:

  • 数值型
  • 字符型 (如果元素值为字符类型,需要加单引号或双引号,防止单个元素中有空格被当成2个元素)
  • 混合型数值加字符

方法一:

 数组名=(value1  value2 value3 value4 ...)
复制代码

方法二:

先定义列表,再引用列表的值定义数组。

 列表名="value1  value2 value3 value4 value5"
 数组名=($列表名)
复制代码

方法三:

用过下标值定义每个元素。

 数组名=([0]=value1  [1]=value2  [2]=value3)
复制代码

方法四:

通过下标值,一个一个定义数组内的每个元素。

同时也可以通过这种方式修改元素的值。

 数组名[0]=value1
 ​
 数组名[1]=value2
 ​
 数组名[2]=value3
 ​
 数组名[3]=value4
复制代码

2定义数组的四种方法.png

3 引用数组的值

1、获取数组的数据列表(所有元素的值)

 echo ${array[@]}
 或
 echo ${array[*]}
复制代码

3-1 数组列表.png

2、查看数组内某一个元素的值

 ${数组名[n]}       //n代表元素的下标
 ​
 echo ${array[0]}   //获取数组内第一个元素的值(下标为0的元素值)
 echo ${array[1]}   //获取数组内第二个元素的值(下标为1的元素值)
复制代码

3-2 数组单个元素.png

3-2 数组单个元素2.png

3、获取数组的长度

 echo ${#数组名[*]}
 echo ${#数组名[@]}
复制代码

3-3 获取数组的长度.png

4、查看数组内最后一个元素的值

方法一:

 echo ${array[-1]}    //获取最后一个元素的值
 echo ${array[-2]}    //获取倒数第二个元素的值
复制代码

3-4数组最后一个元素1.png

方法二:

 length=${#array[@]}    //获取数组的长度
 last=$[$length-1]      //数组的长度减1
 echo ${array[$last]}   //查看最后一个元素值
 #用这种方式查看最后一个元素值,可以判断这个数组的下标是不是完整的。因为如果中间跳过了一个下标,那么此种方法无法获取最后一个元素的值。
 ​
 echo ${array[${#array[@]}]}  //查看下标值为数组长度的元素,如果没有跳过某个下标值,那么这个应该为空。如果不为空,说明跳过了某个下标值,数组下标不完整。
复制代码

3-4数组最后一个元素2.png

3-4数组最后一个元素3.png

4 数组的常用操作

4.1 数组分片(数组截取)

格式:

 ${数组名[@]:起始位置:截取长度}        //起始位置从0算起
 ​
 ${数组名[@]}           //获取整个数组的元素值
 ${数组名[@]:0:3}      //从下标为0的元素开始截取,共截取3个元素。(即截取元素1到元素3)
 ${数组名[@]:2:2}      //从下标为2的元素开始截取,共截取2个元素。(即截取元素3到元素4)
复制代码

示例:

 [root@yuji ~]# arr1=(10 20 30 40 50 60)
 [root@yuji ~]# echo ${arr1[@]}
 10 20 30 40 50 60
 [root@yuji ~]# echo ${arr1[@]:0:3}   //从下标为0的元素开始截取,共截取3个元素
 10 20 30
 [root@yuji ~]# echo ${arr1[@]:2:2}   //从下标为2的元素开始截取,共截取2个元素
 30 40
 [root@yuji ~]# echo ${arr1[@]:2:4}   //从下标为2的元素开始截取,共截取4个元素
 30 40 50 60
复制代码

4.1数组分片.png

4.2 数组元素替换

1、临时替换(不会影响原有数组)

 echo ${数组名[@]/旧字符/新字符}    //会替换所有元素中能匹配到的字符,但这是临时替换,不会改变原有数组的值
复制代码

示例1:将30替换为100

 [root@yuji ~]# arr2=(10 20 30 40)      //定义数组
 [root@yuji ~]# echo ${arr2[@]}         //查看数组内的所有元素
 10 20 30 40
 [root@yuji ~]# echo ${arr2[@]/30/100}     //将30临时替换为100
 10 20 60 40
 [root@yuji ~]# echo ${arr2[@]}         //不会改变原有数组内的元素值
 10 20 30 40
复制代码

4.2数组替换1.png

示例2:将所有的0替换为7

 [root@yuji ~]# echo ${arr2[@]}
 10 20 30 40
 [root@yuji ~]# echo ${arr2[@]/0/7}    //将所有的0替换为7
 17 27 37 47
 [root@yuji ~]# echo ${arr2[@]}        //不会改变原有数组的元素值
 10 20 30 40
复制代码

4.2数组替换2.png

2、重新赋值(永久替换)

 数组名=(${数组名[@]/旧字符/新字符})
复制代码

示例:将30重新赋值为200

 [root@yuji ~]# echo ${arr2[@]}
 10 20 30 40
 [root@yuji ~]# arr2=(${arr2[@]/30/200})     //重新赋值
 [root@yuji ~]# echo ${arr2[@]}                   //会改变原有数组的元素值
 10 20 200 40
复制代码

4.2数组替换3.png

4.3 删除数组

1、删除数组内的某个元素

 unset 数组名[n]        //删除数组内的某一个元素,n为下标值
复制代码

示例:

 [root@yuji ~]# echo ${aa[@]}
 10 20 30 40 
 [root@yuji ~]# unset aa[2]           //删除下标值为2的元素
 [root@yuji ~]# echo ${aa[@]}        //30已被删除
 10 20 40
复制代码

4.3 删除数组1.png

2、删除整个数组

 unset 数组名[*]
 或
 unset 数组名[@] 
复制代码

示例:

 [root@yuji ~]# echo ${aa[@]}
 10 20 40
 [root@yuji ~]# unset aa[@]       //删除整个数组
 [root@yuji ~]# echo ${aa[@]}     //查看数组所有元素值,已为空
 ​
 [root@yuji ~]# echo ${bb[*]}
 1 2 3 4
 [root@yuji ~]# unset bb[*]        //删除整个数组
 [root@yuji ~]# echo ${bb[*]}      //查看数组所有元素值,已为空
 ​
 [root@yuji ~]#
复制代码

4.3 删除数组2.png

4.4 数组扩写,增加数组内的元素

当我们原本定义的数组不能满足我们的需求时,我们需要追加数组内的元素。

 #方法1
 数组名[n]=元素值       //使用下标值直接增加元素
 ​
 #方法2
 数组名+=(元素1 元素2 .....)
 ​
 #方法3
 数组名[数组长度]=元素值     //此种方法必须保证数组元素是完整的,如果某个下标被跳过了,此种方法会替换掉原来的某个元素
 ​
 #方法4
 数组名=("${数组名[@]}" 元素1 元素2 .....)
//引用数组值必须加双引号,不然原数组内有空格的单个元素(例如"zhang san")会被当成2个元素。
//且必须使用@,因为加上双引号之后"$@"表示将所有参数当作个体,而$*表示将所有参数当作一个整体。
复制代码

方法1示例:数组名[n]=元素值

 [root@yuji ~]# echo ${cc[@]}
 10 20 30
 [root@yuji ~]# cc[3]=40
 [root@yuji ~]# cc[4]=50
 [root@yuji ~]# echo ${cc[@]}
 10 20 30 40 50
复制代码

4.4 数组扩写1.png

方法2示例:数组名+=(元素值)

 [root@localhost ~]# echo ${a[@]}
 1 2 3 4 5
 [root@localhost ~]# a+=(6)              //增加元素值6
 [root@localhost ~]# echo ${a[@]}
 1 2 3 4 5 6
 [root@localhost ~]# a+=(7 8 9)          //增加元素值7、8、9
 [root@localhost ~]# echo ${a[@]}
 1 2 3 4 5 6 7 8 9
复制代码

4.4 数组扩写2.png

方法3示例:数组名[数组长度]=元素值

 [root@yuji ~]# echo ${cc[@]}
 10 20 30 40 50
 [root@yuji ~]# cc[${#cc[@]}]=60         //"${#cc[@]"可以获取数组长度
 [root@yuji ~]# echo ${cc[@]}
 10 20 30 40 50 60
复制代码

4.4 数组扩写3.png

方法4示例:数组名=("${数组名[@]}" 元素1 元素2 .....)

 #示例1
 [root@yuji ~]# b=(1 2 3)
 [root@yuji ~]# echo ${b[@]}
 1 2 3
 [root@yuji ~]# b=("${b[@]}" 7 8)     //引用数组的所有元素值一定要使用$@,因为加上双引号之后"$@"表示将所有参数当作个体
 [root@yuji ~]# echo ${b[@]}
 1 2 3 7 8
 [root@yuji ~]# echo ${#b[@]}       //数组长度为5
 5
 [root@yuji ~]# b=("${b[*]}" 9)      //如果使用$*,那么数组内的所有元素会被当作一个整体,即被当成一个元素
 [root@yuji ~]# echo ${b[@]}
 1 2 3 7 8 9
 [root@yuji ~]# echo ${#b[@]}       //数组长度为2
 2
 ​
 #示例2
 [root@yuji ~]# e=("li si")           //当元素类型为字符时,一定要加引号,避免单个元素中有空格被当成2个元素
 [root@yuji ~]# echo ${e[@]}
 li si
 [root@yuji ~]# echo ${#e[@]}
 1
 [root@yuji ~]# e=("${e[@]}" "wang wu" "zhao liu")      //引用数组的元素值时一定要使用双引号,因为单引号无法引用变量值
 [root@yuji ~]# echo ${e[@]}
 li si wang wu zhao liu
 [root@yuji ~]# echo ${#e[@]}
 3
复制代码

4.4 数组扩写4.png

4.4 数组扩写5.png

4.5 使用for遍历输出数组的每个元素值

1、通过数组内的所有元素来遍历。

 [root@yuji ~]# arr=(30 20 40 10 50)
 [root@yuji ~]# echo ${arr[@]}
 30 20 40 10 50
 [root@yuji ~]# for i in ${arr[@]};do echo $i;done
 30
 20
 40
 10
 50
复制代码

4.5遍历数组1.png

2、通过下标值来遍历,这种方法可能会由于数组内元素不完整,某个下标对应的元素值为空。

 [root@yuji ~]# echo ${arr[@]}
 30 20 40 10 50
 [root@yuji ~]# length=${#arr[@]}
 [root@yuji ~]# for ((i=0;i<=length-1;i++))
 > do
 > echo ${arr[$i]}
 > done
 30
 20
 40
 10
 50
复制代码

4.5遍历数组2.png

4.6 查看所有的数组

命令:declare -a

 [root@yuji ~]# declare -a       //查看所有的数组
 declare -a BASH_ARGC='()'
 declare -a BASH_ARGV='()'
 declare -a BASH_LINENO='()'
 declare -a BASH_SOURCE='()'
 declare -ar BASH_VERSINFO='([0]="4" [1]="2" [2]="46" [3]="2" [4]="release" [5]="x86_64-redhat-linux-gnu")'
 declare -a DIRSTACK='()'
 declare -a FUNCNAME='()'
 declare -a GROUPS='()'
 declare -a PIPESTATUS='([0]="0")'
 declare -a arr='([0]="30" [1]="20" [2]="40" [3]="10" [4]="50")'
 declare -a arr2='([0]="10" [1]="20" [2]="200" [3]="40")'
 declare -a b='([0]="1 2 3 7 8" [1]="9")'
 ​
 [root@yuji ~]# declare -a | grep "b"       //过滤出数组b
 declare -a b='([0]="1 2 3 7 8" [1]="9")'
复制代码

4.6查看所有数组1.png

4.6查看所有数组2.png

5 将数组的值传给函数

操作步骤:

 第一步:向函数传入数组的时候需要先把数组转换成列表作为函数的参数使用:
 函数名 ${数组名1[@]}
 ​
 第二步:在函数内把传入的列表重新组合成数组:
 数组名2=($@)
 ​
 第三步:在函数外重新组合成数组:
 数组名3=(`函数名 ${数组名1[@]}`)
复制代码

示例1:获取数组的数据列表作为函数的参数使用

 #!/bin/bash
  
 fun01 () {
 #不能写$1,否则只会输出数组的第一个元素
    echo "函数接收到的参数为:$@"
 #在函数内把传入的列表重新组合成数组
    newarr=($@)
    echo "新数组的值为:${newarr[@]}"
 }
  
 #### main ####
 arr=(1 2 3 4 5)
 echo "原数组的值为:${arr[@]}"
 #把数组转换成列表作为函数的参数使用
 fun01 ${arr[@]}
复制代码

5 数组值传给函数1.png

示例2:使用for循环,令新数组内的每个元素值乘2

方法一:i<=数组长度减1

 #!/bin/bash
 ​
 fun02 () {
 #在函数内把传入的列表重新组合成数组
    newarr=($@)
    echo "新数组的值为:${newarr[@]}"
 # ${#newarr[@]} 为数组的长度
    for ((i=0;i<=${#newarr[@]}-1;i++))
    do
       newarr[$i]=$[${newarr[$i]}*2]
    done
    echo "每个元素值翻倍后为:${newarr[@]}"
 }
 ​
 #### main ####
 arr=(1 2 3 4 5)
 echo "原数组的值为:${arr[@]}"
 #把数组转换成列表作为函数的参数使用
 fun02 ${arr[@]}
复制代码

5 数组值传给函数2.png

方法二:i<=参数个数减1

$# 表示调用函数时函数后面跟的参数个数

 #!/bin/bash
 ​
 fun03 () {
 #在函数内把传入的列表重新组合成数组
    newarr=($@)
    echo "新数组的值为:${newarr[@]}"
 # $#表示调用函数时函数后面跟的参数个数
    for ((i=0;i<=$[$#-1];i++))
    do
       newarr[$i]=$[${newarr[$i]}*2]
    done
    echo "每个元素值翻倍后为:${newarr[@]}"
 }
 ​
 #### main ####
 arr=(1 2 3 4 5)
 echo "原数组的值为:${arr[@]}"
 #把数组转换成列表作为函数的参数使用
 fun03 ${arr[@]}
复制代码

5 数组值传给函数3.png

示例3:把数组的所有元素通过函数求出累加和

把原数组的所有元素传入函数变成新数组后,再求出累加和。

 vim shu1.sh
 ​
 #!/bin/bash
 ​
 myfun () {
 ​
    newarr=($@)
    sum=0
    for i in ${newarr[@]}
    do
       sum=$[sum+i]
    done
    echo "数组内所有元素的累加和是:$sum"
 }
 ​
 ##### main ######
 ​
 arr=(3 1 4 2 6 5)
 ​
 myfun ${arr[@]}
复制代码

5 数组值传给函数4.png