zoukankan      html  css  js  c++  java
  • shell编程基础(二): shell脚本语法之分支语句和循环语句

    一、分支语句

    1、条件测试:test [

    命令test或[可以测试一个条件是否成立,如果测试结果为真,则该命令的Exit Status为0,如果测试结果为假,则命令的Exit Status为1(注意与C语言的逻辑表示正好相反)。例如测试两个数的大小关系:

    [root@VM_0_5_centos test]# var=4
    [root@VM_0_5_centos test]# test $var
    [root@VM_0_5_centos test]# echo $?
    0
    [root@VM_0_5_centos test]# echo $? -gt 5
    0 -gt 5
    [root@VM_0_5_centos test]# test $var -gt 5
    [root@VM_0_5_centos test]# echo $?
    1
    [root@VM_0_5_centos test]# test $var -gt 1
    [root@VM_0_5_centos test]# echo $?
    0
    [root@VM_0_5_centos test]# [ $var -gt 5 ]
    [root@VM_0_5_centos test]# echo $?
    1
    [root@VM_0_5_centos test]# [ $var -gt 1 ]
    [root@VM_0_5_centos test]# echo $?
    0

    虽然看起来很奇怪,但左方括号[确实是一个命令的名字,传给命令的各参数之间应该用空格隔开,比如,$VAR、-gt、3、]是[命令的四个参数,它们之间必须用空格隔开。命令test或[的参数形式是相同的,只不过test命令不需要]参数。以[命令为例,常见的测试命令如下表所示:

    [ -d DIR ]              如果DIR存在并且是一个目录则为真
    [ -f FILE ]             如果FILE存在且是一个普通文件则为真
    [ -z STRING ]           如果STRING的长度为零则为真
    [ -n STRING ]           如果STRING的长度非零则为真
    [ STRING1 = STRING2 ]   如果两个字符串相同则为真
    [ STRING1 != STRING2 ]  如果字符串不相同则为真
    [ ARG1 OP ARG2 ]        ARG1和ARG2应该是整数或者取值为整数的变量,OP是-eq(等于)-ne(不等于)-lt(小于)-le(小于等于)-gt(大于)-ge(大于等于)之中的一个

    和C语言类似,测试条件之间还可以做与、或、非逻辑运算:

    带与、或、非的测试命令
    
    [ ! EXPR ]          EXPR可以是上表中的任意一种测试条件,!表示逻辑反
    [ EXPR1 -a EXPR2 ]  EXPR1和EXPR2可以是上表中的任意一种测试条件,-a表示逻辑与
    [ EXPR1 -o EXPR2 ]  EXPR1和EXPR2可以是上表中的任意一种测试条件,-o表示逻辑或

    例如:

    //测试是否是一个目录
    [root@VM_0_5_centos test]# ls
    $ $  mmzs  -mmzs  test.sh
    [root@VM_0_5_centos test]# [ -d mmzs ]
    [root@VM_0_5_centos test]# echo $?
    1
    //测试逻辑与
    [root@VM_0_5_centos test]# VAR=abc
    [root@VM_0_5_centos test]# [ -d mmzs -a $VAR = 'abc' ]
    [root@VM_0_5_centos test]# echo $?
    0

    注意,如果上例中的$VAR变量事先没有定义,则被Shell展开为空字符串,会造成测试条件的语法错误(展开为[ -d Desktop -a = 'abc' ]),作为一种好的Shell编程习惯,应该总是把变量取值放在双引号之中(展开为[ -d Desktop -a "" = 'abc' ]):

    [root@VM_0_5_centos test]# unset VAR
    [root@VM_0_5_centos test]# [ -d mmzs -a $VAR = 'abc' ]
    -bash: [: too many arguments
    [root@VM_0_5_centos test]# [ -d mmzs -a "$VAR" = 'abc' ]
    [root@VM_0_5_centos test]# echo $?
    1

    2、if/then/elif/else/fi

    和C语言类似,在Shell中用if、then、elif、else、fi这几条命令实现分支控制。这种流程控制语句本质上也是由若干条Shell命令组成的,例如先前讲过的

    if [ -f ~/.bashrc ]; then
        . ~/.bashrc
    fi

    其实是三条命令,if [ -f ~/.bashrc ]是第一条,then . ~/.bashrc是第二条,fi是第三条。如果两条命令写在同一行则需要用;号隔开,一行只写一条命令就不需要写;号了,另外,then后面有换行,但这条命令没写完,Shell会自动续行,把下一行接在then后面当作一条命令处理。和[命令一样,要注意命令和各参数之间必须用空格隔开。if命令的参数组成一条子命令,如果该子命令的Exit Status为0(表示真),则执行then后面的子命令,如果Exit Status非0(表示假),则执行elif、else或者fi后面的子命令。if后面的子命令通常是测试命令,但也可以是其它命令。Shell脚本没有{}括号,所以用fi表示if语句块的结束。见下例:

    [root@VM_0_5_centos test]# vi testif.sh
    查看脚本内容
    [root@VM_0_5_centos test]# cat testif.sh 
    #!/bin/sh
    if [ -f /bin/bash ]
    then 
        echo "/bin/bash is a file"
    else 
        echo "/bin/bash is NOT a file"
    fi
    if :; 
    then 
        echo "always true"; 
    fi
    运行测试
    [root@VM_0_5_centos test]# sh testif.sh 
    /bin/bash is a file
    always true

    :是一个特殊的命令,称为空命令,该命令不做任何事,但Exit Status总是真。此外,也可以执行/bin/true或/bin/false得到真或假的Exit Status。再看一个例子:

    [root@VM_0_5_centos test]# vi testif2.sh
    查看脚本内容
    [root@VM_0_5_centos test]# cat testif2.sh 
    #! /bin/sh
    echo "Is it morning? Please answer yes or no."
    read YES_OR_NO
    if [ "$YES_OR_NO" = "yes" ]; then
            echo "You entered yes!"
    elif [ "$YES_OR_NO" = "no" ]; then
            echo "You entered no!"
    else
        echo "Sorry, $YES_OR_NO not recognized. Enter yes or no."
            exit 1
    fi
    exit 0
    运行测试
    [root@VM_0_5_centos test]# sh testif2.sh 
    Is it morning? Please answer yes or no.
    no
    You entered no!
    [root@VM_0_5_centos test]# sh testif2.sh 
    Is it morning? Please answer yes or no.
    
    Sorry,  not recognized. Enter yes or no.

    上例中的read命令的作用是等待用户输入一行字符串,将该字符串存到一个Shell变量中。

    此外,Shell还提供了&&和||语法,和C语言类似,具有Short-circuit特性,很多Shell脚本喜欢写成这样:

    test "$(whoami)" != 'root' && (echo you are using a non-privileged account; exit 1)
    还是
    test "$(whoami)" == 'root' && (echo you are using a privileged account; exit 1)

    &&相当于“if...then...”,而||相当于“if not...then...”。&&和||用于连接两个命令,而上面讲的-a和-o仅用于在测试表达式中连接两个测试条件,要注意它们的区别,例如,

    [root@VM_0_5_centos test]# VAR=2
    [root@VM_0_5_centos test]# test "$VAR" -gt 1 -a "$VAR" -lt 3
    [root@VM_0_5_centos test]# echo $?
    0

    和以下写法是等价的

    [root@VM_0_5_centos test]# VAR=2
    [root@VM_0_5_centos test]# test "$VAR" -gt 1 && test "$VAR" -lt 3
    [root@VM_0_5_centos test]# echo $?
    0

    3、case/esac

    case命令可类比C语言的switch/case语句,esac表示case语句块的结束。C语言的case只能匹配整型或字符型常量表达式,而Shell脚本的case可以匹配字符串和Wildcard,每个匹配分支可以有若干条命令,末尾必须以;;结束,执行时找到第一个匹配的分支并执行相应的命令,然后直接跳到esac之后,不需要像C语言一样用break跳出。

    [root@VM_0_5_centos test]# vi testcase.sh
    查看脚本内容
    [root@VM_0_5_centos test]# cat testcase.sh 
    #! /bin/sh
    echo "Is it morning? Please answer yes or no."
    read YES_OR_NO
    case "$YES_OR_NO" in
    yes|y|Y|Yes|YES)
        echo "匹配到yes|y|Y|Yes|YES中的一项!";;
    [nN]*)
         echo "匹配到n|N开头的内容";;
    *)
        echo "Sorry, $YES_OR_NO not recognized. Enter yes or no."
        exit 1;;
    esac
    
    exit 0
    运行测试脚本
    [root@VM_0_5_centos test]# sh testcase.sh 
    Is it morning? Please answer yes or no.
    y
    匹配到yes|y|Y|Yes|YES中的一项!

    注意:如果 判断条件*)放在yes|y|Y|Yes|YES)前面,则会和*)成功匹配,因为shell脚本是一行一行的往下执行的。

    使用case语句的例子可以在系统服务的脚本目录/etc/init.d中找到。这个目录下的脚本大多具有这种形式(以/etc/init.d/nfs-kernel-server为例):

        case "$1" in
            start)
                ...
            ;;
            stop)
                ...
            ;;
            reload | force-reload)
                ...
            ;;
            restart)
            ...
            *)
                log_success_msg "Usage: nfs-kernel-server {start|stop|status|reload|force-reload|restart}"
                exit 1
            ;;
        esac

    启动nfs-kernel-server服务的命令是

    [root@VM_0_5_centos test]# sudo /etc/init.d/nfs-kernel-server start

     $1是一个特殊变量,在执行脚本时自动取值为第一个命令行参数,也就是start,所以进入start)分支执行相关的命令。同理,命令行参数指定为stop、reload或restart可以进入其它分支执行停止服务、重新加载配置文件或重新启动服务的相关命令。

    二、循环语句

    4、for/do/done

    Shell脚本的for循环结构和C语言很不一样,它类似于某些编程语言的foreach循环。例如:

    [root@VM_0_5_centos test]# vi tfor.sh
    查看脚本内容
    [root@VM_0_5_centos test]# cat tfor.sh
    #!/bin/sh
    for FRUIT in apple banana pear; do
        echo "I like $FRUIT"
    done
    运行测试脚本
    [root@VM_0_5_centos test]# sh tfor.sh 
    I like apple
    I like banana
    I like pear

    FRUIT是一个循环变量,第一次循环$FRUIT的取值是apple,第二次取值是banana,第三次取值是pear。再比如,要将当前目录下的chap0、chap1、chap2等文件名改为chap0~、chap1~、chap2~等(按惯例,末尾有~字符的文件名表示临时文件),这个命令可以这样写:

    [root@VM_0_5_centos test]# for FILENAME in chap?; do mv $FILENAME $FILENAME~; done
    也可以这样写:
    [root@VM_0_5_centos test]# for FILENAME in `ls chap?`; do mv $FILENAME $FILENAME~; done

    5、while/do/done

    while的用法和C语言类似。比如一个验证密码的脚本:

    [root@VM_0_5_centos test]# vi t1.sh
    查看脚本内容
    [root@VM_0_5_centos test]# cat t1.sh 
    #! /bin/sh
    echo "Enter password:"
    read TRY
    while [ "$TRY" != "secret" ]; do
        echo "Sorry, try again"
    read TRY
    done
    运行脚本测试
    [root@VM_0_5_centos test]# sh t1.sh 
    Enter password:
    123
    Sorry, try again
    secret

    下面的例子通过算术运算控制循环的次数:

    [root@VM_0_5_centos test]# vi t2.sh
    查看脚本内容
    [root@VM_0_5_centos test]# cat t2.sh 
    #! /bin/sh
    COUNTER=1
    while [ "$COUNTER" -lt 10 ]; do
        echo "we try again,now COUNTER=$COUNTER"
        COUNTER=$(($COUNTER+1))
    done
    运行测试脚本
    [root@VM_0_5_centos test]# sh t2.sh 
    we try again,now COUNTER=1
    we try again,now COUNTER=2
    we try again,now COUNTER=3
    we try again,now COUNTER=4
    we try again,now COUNTER=5
    we try again,now COUNTER=6
    we try again,now COUNTER=7
    we try again,now COUNTER=8
    we try again,now COUNTER=9

    Shell还有until循环,类似C语言的do...while循环。本章从略。

    6、break和continue

    break[n]可以指定跳出几层循环,continue跳过本次循环步,没跳出整个循环。

    break跳出,continue跳过。

    三、习题

    1、把上面验证密码的程序修改一下,如果用户输错五次密码就报错退出?

    [root@VM_0_5_centos test]# vi thwork.sh 
    
    查看脚本内容
    [root@VM_0_5_centos test]# cat thwork.sh 
    #! /bin/sh
    COUNTER=1
    echo "Enter password:"
    read TRY
    while [ "$COUNTER" -lt 5 -a "$TRY" != "secret" ]; do
        echo "Sorry, try again"
        COUNTER=$(($COUNTER+1))
    read TRY
    done
    
    运行测试脚本
    [root@VM_0_5_centos test]# sh thwork.sh 
    Enter password:
    w
    Sorry, try again
    w
    Sorry, try again
    w
    Sorry, try again
    w
    Sorry, try again
    w
    [root@VM_0_5_centos test]#
  • 相关阅读:
    笔记35 跨重定向请求传递数
    判断邮箱的正则表达式
    按钮
    async await 的用法
    笔记34 Spring MVC的高级技术——处理multipart形式的数据
    Convert Sorted Array to Binary Search Tree
    Binary Tree Zigzag Level Order Traversal
    Unique Binary Search Trees,Unique Binary Search Trees II
    Validate Binary Search Tree
    Populating Next Right Pointers in Each Node,Populating Next Right Pointers in Each Node II
  • 原文地址:https://www.cnblogs.com/mmzs/p/9300478.html
Copyright © 2011-2022 走看看