一:函数分类 

  1. 本地函数(本地变量)
    local 变量名。本地函数中对变量的修改,赋值只在当前函数运行期间有效。
    func1 () { local name=test;echo "func1:name=$name"; local age=18;echo "func1:age=$age"; echo $$;}

  2. 全局函数(本地变量)
    不加local及declare -i(仅限整数数字) 默认为全局函数,而declare -ig也为全局函数(本地变量)。全局函数对变量的修改及赋值对当前shell均有效。但子Shell不继承
    func1 () {name=test; declare -i age=16; echo "func1:name=$name, func1:age=$age"; }

  3. 环境函数(环境变量)
    declare -xf 环境函数中的变量会对子shell继承 或 export -f


    练习1:使用函数编写脚本,实现打印国际象棋棋盘,要求每个格子由8个空格组成。
    方法一:
    #!/bin/bash
    red() {
            echo -e "\033[41m        \033[0m\c"
    }
    yel() {
            echo -e "\033[43m        \033[0m\c"
    }
    redyel() {
            for ((i=1;i<=4;i++));do
                    for ((j=1;j<=4;j++));do
                            [ "$1" = "-r" ] && { yel;red; } || { red;yel; }
                    done
                    echo
            done
    }

    for ((line=1;line<=8;line++));do
            [ $[$line%2] -eq 0 ] && redyel || redyel -r
    done

    方法二:
    #!/bin/bash
    lvse (){
            echo -e "\e[1;42m        \e[0m\c"
    }
    huangse (){
            echo -e "\e[1;43m        \e[0m\c"
    }
    for i in {1..8};do
            for x in {1..4};do
                    for n in {1..8};do
                            a=`echo $[$[$i+$n]%2]`
                            if [ $a -eq 0 ];then
                                    huangse
                            else
                                    lvse
                            fi
                    done
            echo
            done
    done




二:函数的返回值
    1:return
        0为正确结果
        1-255为错误
    2:exit

        0为正确结果
        1-255为错误


三:unset

unset取消函数定义,仅影响当前shell及其子Shell。

shell编程--函数篇_汉诺塔


练习2:
编写函数,实现OS的版本判断

编写函数,实现取出当前系统eth0的IP地址
编写函数,实现打印绿色OK和红色FAILED
编写函数,实现判断是否无位置参数,如无参数,提示错误


version () {
        ver=$(cat /etc/redhat-release | sed -r "s/.* ([0-9])[.].*/\1/")

        echo "当前版本号为 $ver"

        return 0

}


ipaddr () {

        ip=$(ip a s | grep ens33 | sed -n "2p"| cut -d/ -f1| awk '{print $2}')
        echo $ip

        return 0

}


print_color() {
        echo -e "\033[41mFAILED\033[0m"
        echo -e "\033[42mOK\033[0m"
}


judge_canshu() {

        if [ $# -eq 0 ];then
                echo "请输入至少一个参数"
        else
                echo "共有 $# 个参数"

        fi
}


练习3

在centos5或6上,编写服务脚本/etc/init.d/routed,完成如下要求
(1) 脚本可接受参数:start, stop, restart, status
(2) 如果参数非此四者之一,提示使用格式后报错退出
(3) 如是start:则创建/var/lock/subsys/routed, 并显示“OK”
考虑:如果事先已经启动过一次,则显示routed already runing...
(4) 如是stop:则删除/var/lock/subsys/SCRIPT_NAME, 并显示“OK”
考虑:如果事先已然停止过了,则显示"Faild"
(5) 如是restart,则先stop, 再start
考虑:如果本来没有start,则在stop步骤显示"Faild",而在start步骤显示OK.
(6) 如是status, 则如果/var/lock/subsys/SCRIPT_NAME文件存在,则显示“routed is running...”
如果/var/lock/subsys/SCRIPT_NAME文件不存在,则显示“routed is stopped...”
(7)在所有模式下禁止启动该服务,可用chkconfig 和 service命令管理
(8) 当start该脚本后,会在该主机上配置去往1.1.1.0和2.2.2.0网络的路由去往172.18.0.1.
(9) 当stop该脚本后,会删除(8)的两条路由
(10) 当restart时,会先删除路由,再加上.

#!/bin/bash
# chkconfig: 2345 19 80
# description:routed
if ! [ -d /var/lock/subsys ];then
        mkdir -p /var/lock/subsys &> /dev/null
fi
. /etc/init.d/functions

start () {
        if [ -e /var/lock/subsys/routed ];then
                action "Staring routed: routed already runing" false
        else
                touch /var/lock/subsys/routed
                route add -net 1.1.1.0/24 gw 172.18.0.1
                route add -net 2.2.2.0/24 gw 172.18.0.1
                action "Starting routed:" true
        fi
}
stop () {
        if ! [ -e /var/lock/subsys/routed ];then
                action "Shutting down routed:routed not running" false
        else
                rm -rf /var/lock/subsys/routed
                route del -net 1.1.1.0/24 gw 172.18.0.1
                route del -net 2.2.2.0/24 gw 172.18.0.1
                action "Shutting down routed:" true
        fi
}
status () {
        if [ -e /var/lock/subsys/routed ];then
                echo "routed is running..."
        else
                echo "routed is stopped..."
        fi
}
case $1 in
        start)
                start;;
        stop)
                stop;;

        restart)
                stop
                start;;
        status)
                status;;
        *)
                echo "Usage: $0 {start|stop|status|restart} "
esac

   

练习4

编写脚本/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/ld-linux-x86-64.so.2
(5)每次复制完成一个命令后,不要退出,而是提示用户键入新的要复制的命令,并重复完成上述功能;直到用户输入quit退出
cpcmd () {
cp `which $1|tail -1` /mnt/sysroot`which $1|tail -1` &> /dev/null
ldd `which $1|tail -1`|egrep -o "/lib64/.*"|sed -r 's@/lib64(/.*) .*@\1@' > /mnt/sysroot/test.txt
while read line
do      
        cd /mnt/sysroot/lib64
        if [ -f $line ];then
                continue
        else    
                cp /lib64$line /mnt/sysroot/lib64$line &> /dev/null
        fi
done < /mnt/sysroot/test.txt
}

#!/bin/bash
. /root/bin/myfunctions
while true;do
read -p " 请输入命令:" cmd
cpcmd $cmd
done


练习5:

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

max () {
        if [ $1 -ge $2 ];then
                echo "最大值是:$1"
        else
                echo "最大值是:$2"
        fi


练习6:

斐波那契数列又称黄金分割数列,因数学家列昂纳多·斐波那契以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列: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阶斐波那契数列

fei () {
        if [ $1 -eq 0 ];then
                let f$1=0
                echo $f$1
        fi
        if [ $1 -eq 1 ];then
                let f$1=1
                echo $f$1
        fi
        if [ $1 -gt 1 ];then
                echo $[$(fei $[$1-1])+$(fei $[$1-2])]
        fi
}

#!/bin/bash
. /root/bin/myfunctions
for n in `seq $1`
do
        fei $n
done


练习7:    
    汉诺塔(又称河内塔)问题是源于印度一个古老传说。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘,利用函数,实现N片盘的汉诺塔的移动步骤
han () {
        if [ $1 -eq 1 ];then
                echo " $2 --> $4"
        else
                han $[$1-1] $2 $4 $3
                echo " $2 --> $4 "
                han $[$1-1] $3 $2 $4
        fi
}

#!/bin/bash  
. /root/bin/myfunctions
han $1 'A' 'B' 'C'
   


    大家也可能发现了,练习六,练习七考察的是递归,在最后我们来介绍一下什么是递归,以及遇到递归的思路是什么。

首先:一定有一种可以退出程序的情况,我们也叫作递归出口。

其次:总是在尝试将一个问题化简到更小的规模。

最后:父问题与子问题不能有重叠的部分

如果满足了这三点。我们就可以利用递归来处理问题了。