shell之数组和函数

1、数组

  • 数组分为三类,分别是变量,普通变量和关联数组。

    • ①变量。

      1
      2
      3
      4
      5
      6
      name=alice
      ---------------------
      |a |l |i |c |e |
      ---------------------
      |0 |1 |2 |3 |4 | #索引
      ---------------------
  • ②普通数组,下标是整数。

    1
    2
    3
    4
    5
    6
    books=(linux shell awk openstack docker)
    ---------------------------------------------
    |linux |shell |awk |openstack |docker |
    ---------------------------------------------
    |0 |1 |2 |3 |4 | #索引
    ---------------------------------------------
    • eg:

      1
      2
      3
      [root@centos7 shell]# books=(linux shell awk openstack docker)
      [root@centos7 shell]# echo ${books[3]}
      openstack
  • 关联数组,下标是字符串。

    1
    2
    3
    4
    5
    6
    info=([name]=zhu [sex]=male [age]=22 [height]=170 [skill]=play)
    -----------------------------------------
    |zhu |male |22 |170 |play |
    -----------------------------------------
    |name |sex |age |height |skill | #索引
    -----------------------------------------
    • eg:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      # declare -a表示普通数组,直接在终端输入,可以查看所有的普通数组
      # declare -A表示关联数组,直接在终端输入,可以查看所有的关联数组
      # declare -i表示整数
      [root@centos7 shell]# declare -A info # 先要声明关联数组
      [root@centos7 shell]# info=([name]=zhu [sex]=male [age]=22 [height]=170 [skill]=play)
      [root@centos7 shell]# echo ${info[name]}
      zhu
      [root@centos7 shell]# info+=([college]=gzmtu) # 往数组里增加一个元素
      [root@centos7 shell]# echo ${info[@]} # 查看数组所有元素
      zhu 170 22 play gzmtu male
      [root@centos7 shell]# let info[age]++ # 使某一个下标的值加1
      [root@centos7 shell]# echo ${info[age]}
      23
  • 操作数组:

    • ①定义数组:

      1
      2
      3
      4
      5
      6
      7
      8
      # 方法1:一次赋一个值,即数组名[下标]=变量值
      array[0]=peer
      array[1]=apple

      # 方法2:一次赋多个值
      array=(abc def ghi)
      array=(`cat /ec/passwd`)# 将该文件中的每一个行作为一个元素赋值给数组array
      array=(1 2 3 4 5 "shell" [20]=wangji) # 可以给第20个元素赋值
    • ②查看数组。

      1
      2
      declare -a
      declare -A
    • ③访问数组元素。

      1
      2
      3
      4
      5
      6
      echo ${array[0]}		# 访问数组中的第一个元素
      echo ${array[@]} # 访问数组中的所有元素,等同于echo ${array[*]}
      echo ${#array[@]} # 统计数组元素的个数
      echo ${!array2[@]} # 获取数组元素的下标
      echo ${array[@]:1} # 从数组下标1开始,针对非关联数组
      echo ${array[@]:1:2} # 从数组下标1开始,访问2个元素,针对非关联数组
      • eg:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        [root@centos7 shell]# array=(linux shell awk openstack docker)
        [root@centos7 shell]# echo ${array[0]}
        linux
        [root@centos7 shell]# echo ${array[@]}
        linux shell awk openstack docker
        [root@centos7 shell]# echo ${#array[@]}
        5
        [root@centos7 shell]# echo ${!array[@]}
        0 1 2 3 4
        [root@centos7 shell]# echo ${array[@]:1}
        shell awk openstack docker
        [root@centos7 shell]# echo ${array[@]:1:2}
        shell awk
    • ④遍历数组:通过数组元素的下标进行遍历

      • eg1:while遍历/etc/hosts文件的所有行并输出。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        [root@centos7 shell]# cat cathost.sh
        #!/usr/bin/bash
        while read line
        do
        hosts[++i]=$line
        done </etc/hosts

        echo "hosts first: ${hosts[1]}"
        # 获得数组的下标
        for i in ${!hosts[@]}
        do
        echo "$i: ${hosts[i]}"
        done
        [root@centos7 shell]# bash cathost.sh
        hosts first: 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
        1: 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
        2: ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
      • eg2:for遍历/etc/hosts文件的所有行并输出。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        [root@centos7 shell]# cat cathost.sh 
        #!/usr/bin/bash
        # for见到空格,tab,换行都会分割
        OLD_IFS=$IFS
        # 若在最前面加上IFS=$'\n'就和eg1的结果一样了
        IFS=$'\n'
        for line in `cat /etc/hosts`
        do
        hosts[++j]=$line
        done

        echo "hosts first: ${hosts[1]}"
        # 获得数组的下标
        for i in ${!hosts[@]}
        do
        echo "$i: ${hosts[i]}"
        done
        # 还原分隔符
        IFS=$OLD_IFS
        [root@centos7 shell]# bash cathost.sh
        hosts first: 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
        1: 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
        2: ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
      • eg3:使用Array实现性别统计。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        [root@centos7 shell]# awk '{print $2}' sex.txt |sort |uniq -c # 使用awk统计也行
        2 f
        1 m
        [root@centos7 shell]# cat sex.txt
        bob m
        alice f
        rose f
        [root@centos7 shell]# cat countsex.sh
        #!/usr/bin/bash
        declare -A sex
        while read line
        do
        type=`echo $line |awk '{print $2}'`
        # 把要统计的对象作为数组的下标
        let sex[$type]++
        done < sex.txt

        for i in ${!sex[@]}
        do
        echo "$i: ${sex[$i]}" # 关联数组的下标要加$
        done
        [root@centos7 shell]# bash countsex.sh
        f: 2
        m: 1
      • eg4:统计不同类型shell的数量。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        [root@centos7 shell]# awk -F ":" '{print $NF}' /etc/passwd |sort |uniq -c # 使用awk统计也行
        8 /bin/bash
        1 /bin/sync
        1 /sbin/halt
        39 /sbin/nologin
        1 /sbin/shutdown
        [root@centos7 shell]# cat countpasswd.sh
        #!/usr/bin/bash
        declare -A shells
        while read line
        do
        # -F指使用':'分割,因为passwd每行的数据都是':'分割的,$NF表示最后一列
        type=`echo $line |awk -F ":" '{print $NF}'`
        let shells[$type]++
        done </etc/passwd

        for i in ${!shells[@]}
        do
        echo "$i: ${shells[$i]}"
        done
        [root@centos7 shell]# bash countpasswd.sh
        /sbin/nologin: 39
        /bin/sync: 1
        /bin/bash: 8
        /sbin/shutdown: 1
        /sbin/halt: 1
      • eg5:Array统计TCP连接状态数量。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        #!/usr/bin/bash
        declare -A shells
        type=`ss -an |grep ":80" |awk '{print $2}'`
        for i in $type
        do
        let shells[$i]++
        done

        for i in ${!shells[@]}
        do
        echo "$i: ${shells[$i]}"
        done
        # 每隔2s一直执行该脚本
        执行命令:watch -n2 tcp.sh

        # 第二种方式:使用死循环,最好在最后加个sleep 1;clear
        #!/usr/bin/bash
        while :
        do
        unset shells
        declare -A shells
        type=`ss -an |grep ":80" |awk '{print $2}'`
        for i in $type
        do
        let shells[$i]++
        done

        for i in ${!shells[@]}
        do
        echo "$i: ${shells[$i]}"
        done
        sleep 1;clear
        done

2、函数

  • 函数作用。

    • 完成特定功能的代码片段(块)。
    • 在shell中定义函数可使得代码模块化,便于复用代码。
  • 定义函数。

    • 方法一:

      1
      2
      3
      4
      函数名()
      {
      函数要实现的功能代码
      }
    • 方法二:

      1
      2
      3
      4
      function 函数名
      {
      函数要实现的功能代码
      }
      • eg1:写一个阶乘函数并调用,写死阶乘数。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        [root@centos7 shell]# cat test.sh 
        #!/usr/bin/bash
        factorial()
        {
        factorial=1
        for((i=1;i<=10;i++))
        do
        factorial=$[$factorial * $i]
        done
        echo "10的阶乘:$factorial"
        }
        factorial
        [root@centos7 shell]# bash test.sh
        10的阶乘:3628800
      • eg2:写一个阶乘函数并调用,阶乘数通过变量绑定。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        [root@centos7 shell]# cat test.sh 
        #!/usr/bin/bash
        factorial()
        {
        factorial=1
        for((i=1;i<=$num;i++))
        do
        factorial=$[$factorial * $i]
        done
        echo "$num的阶乘:$factorial"
        }
        # 调用函数之前,定义了一个num变量
        num=5
        factorial
        [root@centos7 shell]# bash test.sh
        5的阶乘:120
      • eg3:写一个阶乘函数并调用,阶乘数通过调用此函数时传入。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        [root@centos7 shell]# cat test.sh 
        #!/usr/bin/bash
        factorial()
        {
        factorial=1
        for((i=1;i<=$1;i++))
        do
        factorial=$[$factorial * $i]
        done
        echo "10的阶乘:$factorial"
        }
        # 这个10会直接传递给函数factorial()的$1,直接执行./factorial.sh
        factorial 10
        [root@centos7 shell]# bash test.sh
        10的阶乘:3628800
      • eg4:写一个阶乘函数并调用,阶乘数通过调用此脚本时传入。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        [root@centos7 shell]# cat test.sh 
        #!/usr/bin/bash
        factorial()
        {
        factorial=1
        for((i=1;i<=$1;i++))
        do
        factorial=$[$factorial * $i]
        done
        echo "10的阶乘:$factorial"
        }
        # 这个$1会直接传递给函数factorial()的$1,直接执行./factorial.sh 10
        factorial $1
        [root@centos7 shell]# bash test.sh 10
        10的阶乘:3628800

        # 注意区分
        [root@centos7 shell]# cat test.sh
        #!/usr/bin/bash
        factorial()
        {
        factorial=1
        for((i=1;i<=$1;i++))
        do
        factorial=$[$factorial * $i]
        done
        echo "$1的阶乘:$factorial"
        }
        # 这个$1会直接传递给函数factorial()的$1,直接执行./factorial.sh 5 8 10
        # 作为函数来讲,$1$2$3都是函数的第一个参数
        factorial $1
        factorial $2
        factorial $3
        [root@centos7 shell]# bash test.sh 5 8 10
        5的阶乘:120
        8的阶乘:40320
        10的阶乘:3628800
  • 函数的返回值。

    • 函数的返回值:函数最后一条命令的执行结果,要么0,要么非0,但是可利用return自定义返回结果,但是不能超过255

    • 程序的返回值:程序最后一条命令的执行结果。

    • return只能返回0-255的数

      • eg1:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        [root@centos7 shell]# cat testreturn.sh
        #!/usr/bin/bash
        fun2()
        {
        read -p "enter num:" num
        return $[2*$num]
        }
        fun2
        # shell的返回码最高是255,如果超过255就会报错
        echo "fun2 return value: $?"
        [root@centos7 shell]# bash testreturn.sh
        enter num:10
        fun2 return value: 20 # 正确答案
        [root@centos7 shell]# bash testreturn.sh
        enter num:200
        fun2 return value: 144 # 错误答案
      • eg2:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        [root@centos7 shell]# cat testreturn.sh
        #!/usr/bin/bash
        fun2()
        {
        read -p "enter num:" num
        # 自定义函数的返回值,叫做函数的输出,可以是字符串
        echo $[2*$num]
        }
        # 将函数的调用结果赋值给变量result
        result=`fun2`
        echo "fun2 return value: $result"
        [root@centos7 shell]# bash testreturn.sh
        enter num:200
        fun2 return value: 400 # 正确答案
      • eg3:函数返回-输出数组变量。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        [root@centos7 shell]# cat testarray.sh 
        #!/usr/bin/bash
        #!/usr/bin/bash
        num=(1 2 3)
        array()
        {
        echo "all parameters: $*"
        # 或者local newarray=($*) 给局部数组赋值
        local newarray=(`echo $*`)
        # $#:代表传入参数的个数
        local i
        for((i=0;i<$#;i++))
        do
        outarray[$i]=$((${newarray[$i]}*5))
        done
        echo "${outarray[*]}"
        }
        array ${num[*]}

        [root@centos7 shell]# bash testarray.sh
        all parameters: 1 2 3
        5 10 15
  • 函数位置参数与脚本程序的位置参数

    • 函数的位置参数:在函数后面加的位置参数。

    • 脚本程序的位置参数:在执行脚本程序时,后面加的参数。

      1
      2
      3
      4
      #函数接受位置参数:$1,$2,...$n
      #函数接收数组变量 $* 或 $@
      #函数将接收到的所有参数赋值给数组 newarray=($*)
      #函数获取参数的个数 $#
      • eg1:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        [root@centos7 shell]# cat testreturn.sh
        #!/usr/bin/bash
        if [ $# -ne 3 ];then
        echo "usage: `basename $0` par1 par2 par3"
        exit
        fi

        fun3()
        {
        echo "$(($1 * $2 * $3))"
        }
        # 函数内部的参数,$1传递给$1,$2传递给$2, $3传递给$3
        result=`fun3 $1 $2 $3`
        echo "result is : $result"
        [root@centos7 shell]# bash testreturn.sh 1 2 3
        result is : 6
      • eg2:往函数中传数组。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        [root@centos7 shell]# cat testarray.sh 
        #!/usr/bin/bash
        num=(1 2 3 4 5)
        # 输出数组的元素
        echo "${num[@]}"

        array()
        {
        # 默认是全局变量,在不同函数之间可以使用,若只是在函数内部生效加local
        factorial=1
        # 如果使用$*则不需要加双引号
        for i in "$@"
        do
        factorial=$[factorial * $i]
        done
        echo "$factorial"
        }
        # 或写成:array ${num[*]}
        array ${num[@]}
        [root@centos7 shell]# bash testarray.sh
        1 2 3 4 5
        120