Bash编程之循环&函数
一、bash循环(for,while,until)
二、测试脚本
三、多分支if
四、case语句的语法格式
五、function函数
六、变量作用域
七、函数递归

一、bash循环(for,while,until)

1.bash脚本编程
    顺序执行
    选择执行
    循环执行
        for,while,until,
        
    进入条件:
        for :列表元素非空
        while :条件测试结果为“真”
        until:条件测试结果为“假”
    退出条件:
        for:列表元素为空
        while:条件测试结果为“假”
        until:条件测试结果为“真”
2.循环控制语句:
        while CONDATION1 ; do
            CMD1
            ...
            if CONDATION2;then
                continue //结束本次循环
            fi
                CMDn
                ...
        done
        示例:求100以内所有偶数之和
3.创建循环体;
        while true ;do
            循环体
        done
        退出方式:某个测试条件满足时,让循环体执行break命令
        
    break:直接跳出循环
    continue:直接进入下一次循环
    sleep 数字[时间单位] //默认是s,d:天,h:小时
    
4.while循环的特殊用法(遍历文件的行)
        while read VAR; don
            循环体
        done < /path/to/somefile
        依次读取/path/from/somefile文件中的每一行,且将某赋值给VAR变量
        
        示例:脚本5
5.for循环的特殊用法
    for ((控制变量初始化;条件判断表达式;控制var的修正语句))
        循环体
    done
    控制变量初始化,仅在循环代码开始运行时执行一次
    控制变量的修正语句;每轮循环结束会先进行控制变量修正运算,而后在根据判断条件判断
    
    
二、测试脚本    
脚本1:求even:偶数,odd:奇数,100以内之和

    #!/bin/bash
    declare -i evensum=0 /
    declare -i i=0
    while [ $i -le 100 ] ; do
            let i++           //前面不能用let $i++
            if  [  $[$i%2] -eq 0 ] ;then  //此处只能使用[ ] 进行引用
                    let evensum+=$i
            else
                    continue
            fi
    done
    echo "Even sum is $evensum"

    //求所有奇数之和2500    

    #!/bin/bash
    declare -i evensum=0
    declare -i i=0
    
    while [ $i -lt 100 ] ; do    //必须是小于100,否则会出错
        let i++
        if  [  $[$i%2] -eq 1 ] ;then
            let evensum+=$i
        else 
            continue 
        fi
    done
    echo "Even sum is $evensum"

脚本2:求和,break方式实现

    #!/bin/bashk
    declare -i oddsum=0
    declare -i i=1
    while true; do
        let oddsum+=$i
        let i+=2
        if [ $i -gt 100 ];then
            echo $oddsum
            break
        fi
    done

脚本3:每个三s获取已经登录的用户信息,如果wolf用户已经登录,则记录于日志中,并退出

    #!/bin/bash
    while true; do
        if who |grep wolf &> /dev/null ; then
            break
        fi
            sleep 3
    done
    echo "$(date +%F-%H-%M) wolf loggin..." >> /tmp/userlog.log

    
脚本4:util实现脚本3

    #!/bin/bash
    until who | grep "wolf" &> /dev/null ; do
        sleep 3
    done
    echo "$(date +%F-%H-%M) wolf loggin..." >> /tmp/userlog.log

    
脚本5:当前os上id号为偶数的用户及其shell

    #!/bin/bash
    declare -i userid
    while read i ;do
            userid=`echo $i | cut -d: -f3`
                //注:同上userid=$(echo $i | cut -d: -f3)
            if [ $[$userid%2] -eq 0 ] ;then
                    echo $i | cut -d: -f1,7
            fi
    done < /etc/passwd
    //另一种实现方式,while循环                      
    #!/bin/bash
    declare -i userid
    while read i ;do
        userid=$(echo $i | cut -d: -f3)
        username=$(echo $i | cut -d: -f1)
        usershell=$(echo $i | cut -d: -f7)
        if [ $[$userid%2] -eq 0 ] ;then
            echo "$username $userid $usershell"
        fi
     done < /etc/passwd


    
脚本6:for的特殊用法

    #!/bin/bash
    declare -i sum=0
    for ((i=1;i<=100;i++));do
            let sum+=$i
    done
    echo "Sum: is .$sum"

脚本7;使用for的特殊用法,打印99表

    #!/bin/bash
    #
    for ((i=1;i<=9;i++));do
        for ((j=1;j<=i;j++));do
            echo -e -n "${i}*${j}=$[$i*$j]\t"
        done
        echo  //这个echo用于换行,因为前面不换行了
    done

三、多分支if
多分支if
    if condation1;then
        ..
    elif
        ...
    else
        ...
    fi
    单分支if
示例1:显示一个菜单给用户
    要求:
        1.提示用户给出自己的选择
        2.正确的选择,则给出相应的信息,否则提示,用户重新选择

    #!/bin/bash
    cat << EOF
    cpu) display cpu information
    mem) display memory information
    disk) display disks information
    quit) quit
    ===============================
    EOF
    read -p "Enter your choice: " opt
    while [ "$opt" != "cpu" -a "$opt" != "mem" -a "$opt" != "disk" -a "$opt" != "disk" -a "$
    opt" != "quit" ] ;do    read -p "Enter your choice: " opt
    done
    if [ "$opt" == "cpu" ] ;then
        lscpu
    elif [ "$opt" == "mem" ] ;then
        free -m
    elif [ "$opt" == "disk" ] ;then
        fdisk -l /dev/sd[a-z]     
    else
        echo "quit"
        exit 0
    fi

四、case语句的语法格式
    case $VAR in  //仅仅支持glob风格的通配符
    PAT1)
        ...
        ;; //双分号结尾
    PAT2)
        ...
        ;; //
    ...
    *)
        ...
        ;;
    esac
    
    注意:    
        case支持glob风格的通配符
            *:任意长度的任意字符
            ?:任意单个字符
            []:范围内的
            a|b :a或者b
case实现示例

    #!/bin/bash
    cat << EOF
    cpu) display cpu information
    mem) display memory information
    disk) display disks information
    quit) quit
    ===============================
    EOF
    read -p "Enter your choice: " opt
    while [ "$opt" != "cpu" -a "$opt" != "mem" -a "$opt" != "disk" -a "$opt" != "disk" -a "$
    opt" != "quit" ] ;do    read -p "Enter your choice: " opt
    done
    
    case $opt in 
    cpu)        //不用加“ ”
        lscpu
        ;;        //最后要加 ;;
    disk)
        fdisk -l /dev/sd[a-z]
        ;;
    mem)
        free -m
        ;;
    *)
        echo "quit"
        exit 0     ;;
    esac

五、function函数
    过程式编程:代码重用
        模块化编程
        结构化编程
    把一段独立功能的代码当做一个整体,并为之一个名字;命名的代码段,此即为函数    
    注意:定义函数的代码段不会自动执行,在调用时执行,所谓调用函数,在代码中给定函数名即可
        函数名出现的任何位置,在代码执行时,都会被自动替换为函数代码
    语法一:
        function f_name () { //function和f_name之间有空格
            ...函数体...            
        }
    语法二:
        f_name() {  //f_name() 中间没有空格
            ...函数体...
        }
    函数的生命周期:每次被调用时,返回时终止
        其状态返回结果:函数体中运行的最后一条命令的状态结果;
        自定义状态返回值:需要使用:return
            return [0-255]
                0:成功
                1-255:失败
        脚本使用:exit #作为其返回值
    示例:脚本4
    
    注意:在函数中,不能使用$1,$2,...因为具有特殊的意义
        因为函数也可以接受参数,
            $1,$2调用的是,传递给函数的参数,而不是脚本的参数
    
    函数返回值
        函数的执行结果返回值;
        (1)使用echo或printf命令进行输出
            printf不会自动换行,相当于echo -n
        (2)函数体中调用的命令的执行结果
            函数的退出状态码
            1.默认取决于函数最后执行的那一条命令
            2.自定义的方法,return
    函数可以接受参数
        传递参数给函数
            1.在函数体中,可以使用$1,$2,...引用传递给函数的参数;
                $*和$@调用所有参数,
                $#引用参数个数
            2.在调用函数时,在函数名后以空白字符分隔给定参数列表即可,例如:a b c d
            
六、变量作用域
    局部变量:作用域是函数的声明周期
        定义:locale VARIABLE=VALUE
    本地变量:作用域是运行脚本的shell的生命周期;作用范围是当前shell程序
        a=VALUE //
    全局变量:
    环境变量:
    
    全局变量和局部变量对比:

        #!/bin/bash
        name=tom
        setname() {
            local name=jerry
            echo "Function name:$name"
        }
        setname
        echo "Shell: $name"
    [root@localhost ~]# bash 2.sh
    Function name:jerry
    Shell: tom

    
七、函数递归 //自己调用自己
    函数间接或者直接就调用自身
    10!=10*9!=10*9*8!=...
    

#!/bin/bash
fact() {
    if [ $1 -eq 0 -o $1 -eq 1 ] ;then //[ $1 -eq 0 ] || [ $1 -eq 1 ] 这样也可以
        echo $1
    else
        echo $[$1*$(fact $[$1-1])]
    fi
}
fact $1
#!/bin/bash
fab() {
    if [ $1 -eq 1 ] ;then
        echo -n "1 "
    elif [ $1 -eq 2 ] ;then
        echo -n  "1 "
    else
        echo -n "$[$(fab $[$1-1]+$(fab $[$1-2])]"
        fi
}        
for i in $(seq 1 $1) ;do
    fab $i
done
echo

服务启动服务框架
    $lockfile,值/var/lock/subsys/SCRIPT_NAME
    1.可以接受的参数start,stop,restart,status四个参数
    2.如果参数非此四个,则显示帮助后退出
    3.start,创建lockfile,并显示启动:stop,则创建lockfile,并显示停止;restart,则先删除此文件,再创建此文件,而后显示重启完成,;
        statue根据lockfile是否存在,显示running/stopped
    注:务必使用case语句

#!/bin/bash
#
# chkconfig: - 50 50
# description test_service_script
prog=$(basename $0)
lockfile=/var/lock/subsys/$prog
case $1 in
start)
    if [ -f $lockfile ] ;then
        echo "$prog is running yet."
    else
        touch $lockfile 
        [ $? -eq 0 ] && echo "Start $prog finished.."
    fi
    ;;
stop)
    if [ -f $lockfile ] ;then
        rm -f $lockfile
        [ $? -eq 0 ] && echo "$prog stopped.."
    else
        echo "$prog is not running.."
    fi
    ;;
restart)
    if [ -f $lockfile ] ; then    
        rm -f $lockfile
        touch $lockfile
        echo "$prog restart finished.."
    else
        touch $lockfile
        echo "$prog start finished.."
    fi
    ;;
status)
    if [ -f $lockfile ] ;then
        echo "$prog is running.."
    else
        echo "$prog is stopped.."    
    fi
    ;;
*)
    echo "Usage: $prog {start|stop|resatrt|status}"
esac                    
    
    cp service /etc/init.d/service
    chmod +w /etc/init.d/service
    chkconfig --add service
    service service start

    
八、function脚本
1.给定一个用户名,获取其id和shell

    #!/bin/bash
    #
    userinfo() {
        if id "$username" &> /dev/null ;then    
            grep "$username" /etc/passwd | cut -d: -f3,7
        else
            echo "No $username such user.."
        fi
    }
    username=$1
    userinfo         //调用该函数,可以调用多次
    username=$2
    userinfo

2.升级版    

    #!/bin/bash
    #
    # chkconfig: - 50 50
    # description test_service_script
    prog=$(basename $0)
    lockfile=/var/lock/subsys/$prog
    start() {
        if [ -f $lockfile ] ;then
            echo "$prog is running yet."
        else
            touch $lockfile 
            [ $? -eq 0 ] && echo "Start $prog finished.."
        fi
    }
    stop() {
        if [ -f $lockfile ] ;then
            rm -f $lockfile
            [ $? -eq 0 ] && echo "$prog stopped.."
        else
            echo "$prog is not running.."
        fi
    }
    restart() {
        if [ -f $lockfile ] ; then    
            rm -f $lockfile
            touch $lockfile
            echo "$prog restart finished.."
        else
            touch $lockfile
            echo "$prog start finished.."
        fi
    }
    status() {
        if [ -f $lockfile ] ;then
            echo "$prog is running.."
        else
            echo "$prog is stopped.."    
        fi
    }
    usage() {
        echo "Usage: $prog {start|stop|resatrt|status}"
    }
    
    case $1 in 
    start)
        start ;;
    stop)
        stop ;;
    restart)
        restart;;
    status)
        status;;
    *)
        usage;;
    esac

    
3.添加10个用户
    添加用户的功能用函数实现,用户名作为参数传递给函数    

#!/bin/bash
#
# 5: user is exists
#
#
adduser() {
    if id $1 &> /dev/null; then
        return 5
    else
        useradd $1
        retval=$?
        return $retval  
    fi
}
for i in {1..10} ;do
        adduser ${1}$i 
        a=$?  //$?不要直接引用,如果需要多次引用的话,进行赋值变量
    if [ $a -eq 0 ] ;then
         echo "useradd succeed..."
    elif [ $a -eq 5 ] ;then
         echo "user ${1}$i is exists ,useradd failure."
    else
         echo "Unknown errors.." 
    fi
done