bash脚本编程--函数

在bash中,函数是由命令和语句结构构成的能够实现特定功能的集合;


为什么要在bash中引入函数?

在bash脚本编写过程中有可能会出现重复且不做任何改变的代码内容,如果这类内容全靠原始代码书写的话不易于排错和优化;因此我们可以选择将此类代码封装在函数中,在适当的场景中可以重复调用执行;


像此类被封装起来的代码块通常称其为模块,也叫函数;


注意:

1.想要使用函数,必须在使用前先定义出来;

2.如果在某个bash脚本中包含了函数体,默认函数体中的各命令和语句不会执行的;只有在调用函数名的时候,才会执行函数体中的命令和语句;

3.通常需要重复执行的代码块或命令集,可以封装成函数;

4.被调用的函数只能在调用的函数的shell中被执行;


定义函数的方法:

函数有两部分组成:

函数名+函数体


函数名:调用函数时所使用的字符串标识;在一个执行环境中,函数名是不允许重复定义的;

函数体:能够实现特定独立功能的shell命令或结构化语句块;


定义的语法结构:

语法一:

function func_name {

func_body

}

语法二:

func_name() {

func_body

}

注意:在语法二的格式中,func_name和()之间绝对不能有空白字符存在;


注意:函数可以在脚本中被定义,也可以在当前shell中通过交互式环境定义;



函数的使用方法:

函数在定义的时候,其函数体中包含的所有命令或结构化语句都不会被执行;只有在函数被调用时,才能执行其函数体中的各命令和语句;


调用方式:在命令行脚本中,通过直接给出的函数名的方式进行函数调用;


通常可以将常用的函数存放在专门用于保存函数的文件中,如果想要调用这个文件中已经被定义保存的函数,只需要在命令行或脚本中,使用source命令(.命令)加载文件内容到当前shell中,然后在直接使用函数名调用函数即可;


函数的撤销:

unset命令

格式:# unset func_name


注意:可以使用set命令查看当前已经定义并生效的函数;


函数的返回值:

两种返回值:

函数的执行结果返回值:

1.在函数体中所添加的命令有标准输出;

2.在函数体中使用echo或printf命令强制输出返回信息;


函数的执行状态返回值:

1.默认情况下其状态返回值为函数体中最后一条命令的状态返回值;

2.自定义状态返回值或者叫做退出码;

return命令:

return: return [n]

    从一个 shell 函数返回


    n:0-255(1,2,127为系统保留的状态码,尽量不用)

    0:表示无错误返回

    1-255:表示有错误返回;

    注意:在函数被调用执行时,一旦遇到return命令则不会再继续执行函数体中其他的后续命令,立刻结束此次函数的调用执行;



    函数的生命周期:

    一般来讲,从函数被调用时开始,知道函数体中所有的命令和结构化语句全部执行完成或遇到return命令,函数的调用结束;


    函数的实参:

    对应的bash函数来说,没有形参,只有实参;

    bash函数的实参是使用$1,$2...位置变量来提供数据的;


    func_name pos1 pos2 ...


    可以使用$@或者$*表示全部的参数列表;

    可以使用$#计算参数的个数;


    注意:为函数提供参数时使用的位置变量,是调用函数名时在函数名后面的对用位置上的参数信息;与脚本的位置参数不是一回事;


    变量:

    函数被调用时,必须在某特定的shell中被调用,因此,函数中可以继承并识别出环境变量和由调用函数shell定义的本地变量;


    在函数中还可以定义局部变量;而局部变量仅在函数的生命周期内有效;在结束函数执行之前,应该撤销所有该函数定义的局部变量;


    局部变量的定义方法:

    local VAR_NAME=VALUE


    变量的替换方式:

    前提:定义环境变量:

    export MYVAR=qhdlink


示例:

#!/bin/bash

#区别全局变量、本地变量、局部变量;

testvar() {

  #local命令定义局部变量,只在局部有效;

  local MYVAR=chinalink

  echo "Internal function: $MYVAR"

}

#全局变量

echo "Global variables: $MYVAR"

MYVAR=link

#本地变量

echo "External function, $MYVAR"

#调用局部变量

testvar




函数的递归调用:

广义:在一个函数中调用另一个函数

狭义:在函数体中调用函数自身;

直接调用:

func1(){

func1

}

间接调用:

func2(){

func1

}


func1(){

func2

}



函数的直接递归调用示例1:

计算某个数字的阶乘:

利用for循环:

#!/bin/bash

#本脚本计算某个数字的阶乘:

#定义fact为1;

fact=1

#如果给定的数字等于0,就输出0的阶乘为$fact为1,如果数字等于1就输出1的阶乘为$fact为1,否则就开始计算输入数字的阶乘;

if [ $1 -eq 0 ] ; then

  echo "0! is $fact"

elif [ $1 -eq 1 ] ; then

  echo "1! is $fact"

else

  #利用for循环计算阶乘,从1开始一直到$1;

  for I in $(seq $1) ; do

  #阶乘是n!=1×2×3×...×n,所以$1的阶乘是$1!=1×2×3×...×$1;

    let fact=$[fact*$I]

  done

  echo "${1}! is $fact"

fi

 

#回收变量;

unset fact I


利用函数递归调用:

#!/bin/bash

#本脚本利用函数计算某个数字的阶乘:

#定义函数fact;

fact(){

  #如果给定的数字等于0或1就执行显示数字的阶乘等于1,否则就利用函数计算给定数字为非0非1的阶乘;

  if [ $1 -eq 0 ] || [ $1 -eq 1 ] ; then

    echo 1

  else

  #用$1减去$($1-1)的阶乘,n的阶乘就是nx(n-1)!的阶乘,n-1的阶乘就是(n-1)x(n-2)!的阶乘,依次类推;

    echo "$[$1*$(fact $[$1-1])]"

  fi

}


echo "${1}! is $(fact $1)"



函数的直接递归调用示例2:

斐波那波数列(黄金分割数列)

1 1 2 3 5 8 13 21 34 55 ...

#!/bin/bash

#本脚本显示斐波那波数列(黄金分割数列);

#定义函数fabonacci;

fabonacci(){

  #如果给定的数字为1或者2就显示数列1,否则就利用函数计算给定数字为非1非2的黄金分割数;

  if [ $1 -eq 1 ] || [ $1 -eq 2 ] ; then

    echo -n "1 "

  else

    #函数的式子来自黄金分割数列的规律,利用函数的自我调用来计算显示黄金分割数,给定数字的黄金分割数列规律是:给定数字等于给定数字在数列中的前两个数字之和即如下规律;

    echo -n "$[$(fabonacci $[$1-1])+$(fabonacci $[$1-2])] "

  fi

}


for I in $(seq $1) ; do

  fabonacci $I

done

echo


函数的直接递归调用示例3:

汉诺塔

#!/bin/bash

#本脚本显示给出汉诺塔层数的搬运次数;

#定义步数step为0;

step=0

#定义函数move为挪动盘子的方法:这里简化利用最简单的只有一个盘子的时候,先把盘子挪动到第二个柱子,再把盘子从第二个柱子挪动到第三个盘子;

move(){

  #随着盘子的挪动,步数也要进行增长;

  let step++

  #先把盘子挪动到第二个柱子,再把盘子从第二个柱子挪动到第三个盘子;

  echo "$step: move disk $1 $2 --> $3"

}

#定义函数hanoi为移动规律;

hanoi(){

  #当塔为1层时,直接先把盘子挪动到第二个柱子,再把盘子从第二个柱子挪动到第三个盘子,否则利用汉诺塔规律和函数的自我调用实现多层数的步数计算;

  if [ $1 -eq 1 ] ; then

    move $1 $2 $4

  else

  #除了塔底层最后一个大盘子,将其他的盘子整合到一起由第一个柱子整体挪动到第二个柱子,再将底层的最后一个大盘子挪到第三个柱子,再将第二个柱子上的其他盘子整合挪到第三个柱子:用hanoi挪到两次,再用move挪一次,再使用调用hanoi函数完成最后的把其他盘子整体由第二个柱子移动到第三个柱子;

    hanoi "$[$1-1]" $2 $4 $3

    move $1 $2 $4

    hanoi "$[$1-1]"  $3 $2 $4

  fi

}

#传递四个参数:层数,A柱,B柱,C柱;

hanoi $1 A B C


#回收变量

unset step A B C