• linux之基础shell脚本编程3 函数和数组


    linux之基础shell脚本编程1  基础变量赋值

    linux之基础shell脚本编程2 if语句循环判断

    linux之基础shell脚本编程3 函数数组

    linux之基础shell脚本编程4 字符串操作,变量赋值,配置用户环境 

    本章主要写shell有关函数,数组使用

    七 函数

    7.1 函数的介绍

    函数function是由若干条shell命令组成的语句块,实现代码重用和模块化编程。 

    它与shell程序形式上是相似的,不同的是它不是一个单独的 进程,不能独立运行,而是shell程序的一部分。 

    函数和shell程序比较相似,区别在于: Shell程序在子Shell中运行,而Shell函数在当前Shell中运行。因此在当前Shell中,函数可以对shell中变量进行修改

    7.2 函数的定义

    函数由两部分组成:函数名和函数体。 

    1. #语法一: 
    2. function f_name {
    3.    ...函数体... 
    4. #语法二: 
    5. function f_name () {
    6.    ...函数体... 
    7. #语法三: 
    8. f_name (){
    9.    ...函数体... 
    10. }

    7.3 函数的定义和使用:

    可在交互式环境下定义函数 

    可将函数放在脚本文件中作为它的一部分 

    可放在只包含函数的单独文件中 

    调用:函数只有被调用才会执行; 

    调用:给定函数名,函数名出现的地方,会被自动替换为函数代码

    函数的生命周期:被调用时创建,返回时终止

    函数一直保留到用户从系统退出,或执行了 unset name 命令

    7.4 函数有两种返回值

    函数的执行结果返回值: 

      (1) 使用echo或printf命令进行输出 

      (2) 函数体中调用命令的输出结果 

    函数的退出状态码: 

      (1) 默认取决于函数中执行的最后一条命令的退出状态码 

      (2) 自定义退出状态码,其格式为: 

           return  从函数中返回,用最后状态命令决定返回值 

           return 0  无错误返回。 

           return 1-255  有错误返回

    7.5 在脚本中定义函数

    函数在使用前必须定义,因此应将函数定义放在脚本开始部分,直至shell首次发现它后才能使用 

    调用函数仅使用其函数名即可

    比如:

    1. #!/bin/bash
    2. hello(){
    3.     echo "hello to linux"
    4. }
    5. hello  #直接调用函数

    7.6 使用函数文件

    可以将经常使用的函数存入函数文件,然后将函数文件载入shell。 

    文件名可任意选取,但最好与相关任务有某种联系。例如:functions.main 

    一旦函数文件载入shell,就可以在命令行或脚本中调用函数。可以使用set命令查看所有定义的函数,其输出列表包括已经载入shell的所有函数。

    若要改动函数,首先用unset命令从shell中删除函数。改动完毕后,再重新载入此文件。

    7.6.1 创建函数文件

    1. [root@centos7 test]# vim functions.main
    2. #!/bin/bash
    3. #functions.main
    4. #author nineven
    5. findit(){
    6.     if [ $# -lt 1 ] ; then
    7.         echo "Usage:findit file" 
    8.         return 1
    9.     fi
    10.     find / -name $1 -print
    11. }

    7.6.2 载入函数文件

    函数文件已创建好后,要将它载入shell 

    定位函数文件并载入shell的格式: . filename 或 source   filename  

    注意:此即<点> <空格> <文件名> 这里的文件名要带正确路径 

    比如:上例中的函数,可使用如下命令:

    [root@centos7 test]# . ./functions.main

    [root@centos7 test]# source ./functions.main

    7.6.3 检查载入函数

    使用set命令检查函数是否已载入

    set命令将在shell中显示所有的载入函数

    比如:

    7.6.4 执行shell函数

    要执行函数,简单地键入函数名即可

    1. [root@centos7 test]# findit te.sh
    2. /testdir/shell/test/te.sh
    3. /root/Desktop/tmp/testdir/shell/test/te.sh
    4. /root/te.sh
    5. /tmp/testdir/shell/test/te.sh
    6. [root@centos7 test]#

    7.6.5 删除shell函数

    删除函数,使其对shell不可用 。使用unset命令完成此功能.

    命令格式为: unset  function_name

    比如: $unset findit 再键入set命令,函数将不再显示

    1. [root@centos7 test]# findit te.sh
    2. /testdir/shell/test/te.sh
    3. /root/Desktop/tmp/testdir/shell/test/te.sh
    4. /root/te.sh
    5. /tmp/testdir/shell/test/te.sh
    6. [root@centos7 test]
    7. [root@centos7 test]# unset findit
    8. [root@centos7 test]# findit te.sh
    9. bash: findit: command not found...
    10. [root@centos7 test]#

    7.7 函数参数

    函数可以接受参数,传递参数给函数,调用函数时,在函数名后面以空白分隔给定参数列表即可;例如“func arg1 arg2 ...” 在函数体中当中,可使用$1, $2, ...调用这些参数;还可以使用$@, $*, $#等特殊变量

    7.8 函数变量

    变量作用域:

     环境变量:当前shell和子shell有效

     本地变量:只在当前shell进程有效,为执行脚本会启动,专用子shell进程;因此,本地变量的作用范围是当前shell脚本程序文件,包括脚本中的函数

     局部变量:函数的生命周期;函数结束时变量被自动销毁 

    注意:如果函数中有局部变量,如果其名称同本地变量,使用局部变量。 

    在函数中定义局部变量的方法 local NAME=VALUE

    7.9 函数递归:

    函数递归: 

      函数直接或间接调用自身 

      注意递归层数 

    递归实例: 阶乘是基斯顿·卡曼于1808年发明的运算符号,是数学术语 一个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且有0的阶乘为1。自然数n的阶乘写作n! ; n!=1×2×3×...×n。 

    阶乘亦可以递归方式定义:0!=1,n!=(n-1)!×n ; n!=n(n-1)(n-2)...1 ; n(n-1)! = n(n-1)(n-2)!

    比如:

    1. #!/bin/bash 
    2. fact() { 
    3.   if [ $1 -eq 0 -o $1 -eq 1 ]; then 
    4.     echo 1 
    5.   else 
    6.     echo $[$1*$(fact $[$1-1])] fi } 
    7. fact $1


    实战训练9

    1、写一个服务脚本/root/bin/testsrv.sh,完成如下要求 

    (1) 脚本可接受参数:start, stop, restart, status 

    (2) 如果参数非此四者之一,提示使用格式后报错退出 

    (3) 如是start:则创建/var/lock/subsys/SCRIPT_NAME, 并显示“启动成功” 考虑:如果事先已经启动过一次,该如何处理? 

    (4) 如是stop:则删除/var/lock/subsys/SCRIPT_NAME, 并显示“停止完成” 考虑:如果事先已然停止过了,该如何处理? 

    (5) 如是restart,则先stop, 再start 考虑:如果本来没有start,如何处理? 

    (6) 如是status, 则如果/var/lock/subsys/SCRIPT_NAME文件存在,则显示 “SCRIPT_NAME is running...” 如果/var/lock/subsys/SCRIPT_NAME文件不存在,则显示“SCRIPT_NAME is stopped...” 其中:SCRIPT_NAME为当前脚本名 

    (7)在所有模式下禁止启动该服务,可用chkconfig 和 service命令管理

    1. #!/bin/bash
    2. syon(){
    3.     if [  -e /var/lock/subsys/$0 ];then
    4.         echo 1
    5.     else
    6.         echo 0
    7.     fi
    8. }
    9. starts(){
    10.     if [ $(syon) -eq 1 ];then
    11.         echo "服务已经启动过了"
    12.     else
    13.         if  touch /var/lock/subsys/$0 &> /dev/null ;then
    14.             echo "$1启动成功"
    15.         else
    16.             echo "$1启动失败"
    17.             exit 1
    18.         fi
    19.     fi
    20. }
    21. stops(){
    22.     if [ $(syon) -eq 0 ] ;then
    23.          echo "服务没有启动"
    24.     else
    25.           if  rm -f /var/lock/subsys/$0 &>/dev/null ;then
    26.             echo "停止成功"     
    27.           else
    28.             echo "停止失败$1"
    29.             exit 1
    30.           fi
    31.     fi
    32. }
    33. restarts(){
    34.     stops ",服务无法重新启动"
    35.     starts "服务正在重新重启中,"
    36. }
    37. statuss(){
    38.     if [ $(syon) -eq 0 ] ;then
    39.         echo "服务没有启动"
    40.     else
    41.         echo "服务已经启动过了"
    42.     fi
    43. }
    44. case $1 in
    45.     start)
    46.         starts
    47.         ;;
    48.     stop)
    49.         stops
    50.         ;;
    51.     restart)
    52.         restarts
    53.         ;;
    54.     status)
    55.         statuss
    56.         ;;
    57.     *)
    58.         echo "error ,please Usage: $0 {start|stop|restart|status}"
    59. esac

    2、编写一个脚本/root/bin/copycmd.sh 

    (1) 提示用户输入一个可执行命令名称; 

    (2) 获取此命令所依赖到的所有库文件列表 

    (3) 复制命令至某目标目录(例如/mnt/sysroot)下的对应路径下; 

    如:/bin/bash ==> /mnt/sysroot/bin/bash /usr/bin/passwd ==> /mnt/sysroot/usr/bin/passwd 

    (4) 复制此命令依赖到的所有库文件至目标目录下的对应路径下: 

    如:/lib64/ld-linux-x86-64.so.2 ==> /mnt/sysroot/lib64/ldlinux-x86-64.so.2 

    (5)每次复制完成一个命令后,不要退出,而是提示用户键入新的要复制的命令,并重复完成上述功能;直到用户输入quit退出

    1. #!/bin/bash
    2. flag=1
    3. read -p "please input a path that you want to copy : " mubiaodir
    4. panduanlujing(){
    5.     path=$(dirname $mubiaodir$ki)
    6.     if [ ! -e $path ];then
    7.         mkdir -p $path
    8.     fi
    9.     cp -f $ki $mubiaodir$ki &> /dev/null || flag=0
    10. }
    11. kuwenjian(){
    12.     for ki in $(ldd $(which --skip-alias $cmdname) | sed -rn "s@.*[[:space:]]([/].*)[[:space:]].*@\1@p") $(which --skip-alias $cmdname);do
    13.         panduanlujing
    14.     done
    15. }
    16. while true ;do
    17. read -p "please input a command name , quit is exit:" cmdname
    18. if [ "$cmdname" == "quit" -o "$cmdname" == "exit" ];then
    19.     break
    20. else
    21.     if (ldd $(which --skip-alias $cmdname)) &> /dev/null ;then
    22.         kuwenjian
    23.         [  $flag -eq 1 ] && echo "复制成功" || echo "权限不足,复制失败"
    24.     else
    25.         echo "错误的命令名称"
    26.     fi
    27. fi
    28. done
     
    

    3、写一个函数实现两个数字做为参数,返回最大值 

    1. #!/bin/bash
    2. maxnum(){
    3. expr $1 + $2 &> /dev/null && echo $(( $1 > $2 ? $1 : $2 )) || echo "参数错误"
    4. }
    5. maxnum $1 $2

    4、写一个函数实现数字的加减乘除运算,例如输入 1 + 2,,将得出正 确结果 

    1. #!/bin/bash
    2. yunsuan(){
    3.     case $key in
    4.         *)
    5.             echo "$a$key$b=$[a $key b]"
    6.             ;;
    7.         -)
    8.             echo "$a$key$b=$[a $key b]"
    9.             ;;
    10.         +)
    11.             echo "$a$key$b=$[a $key b]"
    12.             ;;
    13.         /)
    14.             echo "$a$key$b=$[a $key b]"
    15.             ;;
    16.     esac
    17. }
    18. read -p "please input agr1 [+|-|*|/] agr2 :" a key b
    19. if [ "$key" == "*" ];then
    20.     if expr $a \* $b &> /dev/null ;then
    21.             yunsuan
    22.     else
    23.             echo "输入错误"
    24.     fi
    25. else
    26.     if  expr $a $key $b  &> /dev/null ;then
    27.         yunsuan
    28.     else
    29.         echo "输入错误"
    30.     fi
    31. fi

    5、斐波那契数列又称黄金分割数列,因数学家列昂纳多·斐波那契以兔子 繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0、1 、1、2、3、5、8、13、21、34、……,斐波纳契数列以如下被以递归的 方法定义:F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2)(n≥2) 写一个函数,求n阶斐波那契数列 

    1. #!/bin/bash
    2. fei(){
    3.     if [ $1 -eq 0 ] ;then
    4.         echo 0
    5.     elif [ $1 -eq 1 ];then
    6.         echo 1
    7.     else
    8.         echo $[$(fei $[$1-1]) + $(fei $[$1-2])]
    9.     fi
    10. }
    11. if [ $# -eq 0  ] ;then
    12.     echo "请传一个个整数作为参数"
    13. else
    14.     fei $[$1-1]
    15. fi
    1. #!/bin/bash
    2. a=0
    3. b=1
    4. fei(){
    5. for((i=3;i<=$1;i++)){
    6.     let c=a+b,a=b,b=c
    7.     echo -n "$c "
    8. }
    9. }
    10. if expr $1 + 1 &> /dev/null ;then
    11. if [ $1 -eq 1 ];then 
    12.     echo $a
    13. elif [ $1 -eq 2 ];then
    14.     echo $a $b
    15. elif [ $1 -ge 3 ];then
    16.     echo $a $b `fei $1`
    17. else
    18.     echo error
    19. fi
    20. else
    21. echo error
    22. fi

    6、汉诺塔(又称河内塔)问题是源于印度一个古老传说。大梵天创造世 界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着 64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放 在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间 一次只能移动一个圆盘。 利用函数,实现N片盘的汉诺塔的移动步骤

    1. #!/bin/bash
    2. move(){
    3.     echo  "将第$1个盘子从 $2 --> $3 "
    4. }
    5. han(){
    6.     if [ $1 -eq 1 ];then
    7.         move 1 $2 $4
    8.     else
    9.         han $[$1-1] $2 $4 $3
    10.         move $1 $2 $4
    11.         han $[$1-1] $3 $2 $4
    12.     fi
    13. }
    14. read -p "请输入盘数:" n
    15. han $n a b c


    八 数组

    变量:存储在单个元素的内存空间

    属组:存储多个元素的连续的内存空间,相当于多个变量的集合

    数组名和索引

     索引:编号从0开始,属于数值索引

     注意:索引可支持使用自定义格式,而不仅是数值格式,即为关联索引,bash4.0版本之后开始支持

    bash的属组支持稀疏格式(索引不连续)

    8.1 定义数组

    1,声明数组:

    1.  declare -a ARRAY_NAME
    2.  declare -A ARRAY_NAME:关联数组

    2,数组元素的赋值:

     (1)一次只赋值一个元素

        ARRAY_NAME[INDEX]=VALUE

    1. week[0]="oneday"
    2. week[2]="twoday"

     (2)一次性赋值全部元素

    ARRAY_NAME=("one" "two" "three")

     (3)只赋值特定元素

    ARRAY_NAME=([0]="val1" [2]="val2"...)

      (4)交互式数组赋值

    read -a ARRAY

    3,引用数组

    引用数组元素: ${ARRAY_NAME[INDEX]}  省略[INDEX]表示引用下标为0的元素

    1. ${ARRAY_NAME[*]}  #输出数组所有内容
    2. ${ARRAY_NAME[@]}  #输出数组所有内容

    数组的长度(数组中元素的个数)

    1. ${#ARRAY_NAME[*]}
    2. ${#ARRAY_NAME[@]}

    比如:生成10个随机数保存于数组中,并找出其最大值和最小值

    1. #!/bin/bash
    2. declare -a num
    3. let num[0]=min=max=$RANDOM
    4. for i in {1..9};do
    5.     num[$i]=$RANDOM
    6.     [ ${num[$i]} -gt $max ] && max=${num[$i]}
    7.     [ ${num[$i]} -lt $min ] && min=${num[$i]}
    8. done
    9. echo ${num[*]}
    10. echo "max=$max  min=$min"

    练习: 编写脚本,定义一个数组,数组中的元素是/var/log目录下所有以.log结尾的文件;要统计其下标为偶数的文件中的行数之和 

    1. #!/bin/bash
    2. declare -i lines=0
    3. declare -a filename
    4. filename=(/var/log/*.log)
    5. for i in $(seq 0 $[${#filename[*]}-1]);do
    6.     if [ $[$i%2] -eq 0 ];then
    7.         let lines+=$(cat ${filename[$i]} | wc -l)
    8.     fi  
    9. done
    10. echo "lines is $lines"

    4,数组数据处理

    a,引用数组中的元素

        所有元素: ${ARRAY_NAME[*]}  ${ARRAY_NAME[@]}

        数组切片: ${ARRAY_NAME[@]:offset:number}

            offset: 要跳过的元素个数

            number: 要取出的元素个数

         取出偏移量之后的所有元素: ${ARRAY_NAME[@]:offset}

    b,向数组中追加元素: ARRAY_NAME[${#ARRAYNEM[*]}]

    c,删除数组中的某元素:导致稀疏格式:unset ARRAY_NAME[INDEX]

    d,关联数组:

    1. declare -A ARRAY_NAME
    2. ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2'...)



    实战训练10

    1,生成10个随机数,采用冒泡算法进行升序或降序排序

    1. #!/bin/bash
    2. declare -i n=10
    3. maopao(){
    4.     for i in $(seq 1 $[$n-1]);do
    5.     for j in $(seq 0 $[$n-$i-1]);do
    6.         if [ ${k[$j+1]} -gt ${k[$j]} ];then
    7.             tmp=${k[$j+1]}
    8.             k[$j+1]=${k[$j]}
    9.             k[$j]=$tmp
    10.         fi
    11.     done
    12.     done
    13. }
    14. declare -a k
    15. for i in  $(seq 0 $[$n-1]);do
    16.     k[$i]=$RANDOM
    17. done
    18. echo "原来随机数:${k[*]}"
    19. maopao
    20. echo "排序后的数:"${k[*]}

    2,手动输入数字排序

    1. #!/bin/bash
    2. echo  "please input  number and split with space"
    3. read -a k
    4. n=${#k[*]}
    5. for((i=0;i
    6. for((j=0;j
    7. ${k[$j]} -gt ${k[$j+1]} ] && { tmp=${k[$j]};k[$j]=${k[$j+1]};k[$j+1]=$tmp; }
    8. }
    9. }
    10. echo ${k[@]}

  • 相关阅读:
    一张图进阶 RocketMQ - 消息存储
    开源协作开发者内容平台Vrite
    iOS xcframework项目提示“ld: framework not found”
    excel提取单元格中的数字
    在vue3中使用el-tree-select做一个树形下拉选择器
    (181)Verilog HDL:设计一个计数器count_clock
    Postman接收列表、数组参数@RequestParam List<String> ids
    第四章 网络层
    redis zset score 求和
    嵌入式C语言自我修养《内存堆栈管理》学习笔记
  • 原文地址:https://blog.csdn.net/ly1358152944/article/details/126290052