条件判断命令: if-then, test, case

作者:jicanmeng

时间:2016年05月12日


  1. if-then语句
  2. if-then-else语句
  3. if-then-elif语句
  4. test命令
    1. test命令比较数值
    2. test命令比较字符串
    3. test命令比较文件
  5. 复合条件测试
  6. if-then的高级特性
    1. 使用双圆括号
    2. 使用双方括号
  7. case命令

1. if-then语句

bash shell中,最基本的结构化命令就是if-then语句了。格式如下:

if command
then
    commands
fi

也常常用下面的格式:

if command; then
    commands
fi

或者

if command; then commands; fi

如果if后面的command的退出状态码为0(即该命令成功运行),那then后面的commands就会执行。例如:

[jicanmeng@andy tmp]$ if date
                    > then
                    > echo "it worked"
                    > fi
                    Tue May 10 21:36:36 CST 2016
                    it worked
                [jicanmeng@andy tmp]$ 

2. if-then-else语句

if-then-else语句的格式如下:

if command
then
    commands
else
    commands
fi

当然和if-then语句一样,也可以使用简化的形式,只是需要注意在command后面加分号;。举例如下:

[jicanmeng@andy tmp]$ cat 2-if-then-else.sh
                    #!/bin/bash

                    testuser=badtest
                    if grep $testuser /etc/passwd
                    then
                          echo The files for user $testuser are:
                          ls -a /home/$testuser/.b*
                    else
                          echo "The user name $testuser does not exist on this system"
                    fi
                [jicanmeng@andy tmp]$ bash 2-if-then-else.sh
                    The user name badtest does not exist on this system
                [jicanmeng@andy tmp]$ 

3. if-then-elif语句

有多个条件需要判断的时候,可以用if-then-elif语句。它的格式如下:

if command
then
    commands
elif command
then
    commands
elif command
then
    commands
fi

其实和c语言的语法差不多,这里就不举例子了。

4. test命令

目前为止,你所了解到的if语句中的命令都是普通shell命令。你可能想问,if-then语句是否能测试跟命令的退出状态码无关的条件。

答案是不能。if-then语句运行的唯一标准就是if语句后面接的shell命令是否执行成功。

但在实际的应用中,if语句常常用来比较两个值的大小,例如if a > b。但是a>b不是shell命令,不满足if-then语句的要求。为了解决这个问题,bash shell引入了test命令。test命令的格式非常简单:

if test condition
then
    commands
fi

bash shell提供了另一种在if-then语句中使用test命令的方法:

if [ condition ]
then
    commands
fi

这两种形式是完全等价的。这里需要特别注意,你必须在左括号右侧和右括号左侧各加一个空格,否则会报错。

test命令可以用来判断3类条件:1.数值比较; 2.字符串比较; 3.文件比较。下面详细说一说。

4.1 test命令比较数值

使用test命令进行数值比较时,使用如下符号:

符号 描述
n1 -eq n2 检查n1是否等于n2
n1 -ne n2 检查n1是否不等于n2
n1 -ge n2 检查n1是否大于等于n2
n1 -gt n2 检查n1是否大于n2
n1 -le n2 检查n1是否小于等于n2
n1 -lt n2 检查n1是否小于n2

数值条件测试可以用在数字和变量上。举例如下:

[jicanmeng@andy tmp]$ cat 3-test-number.sh
                    #!/bin/bash

                    var1=10
                    var2=11

                    if test $var1 -gt 5
                    then
	                      echo "The test value $var1 is greater than 5"
                    fi

                    if [ $var1 -eq $var2 ]
                    then
	                      echo "The values are equal"
                    else
	                      echo "The values are different"
                    fi
                [jicanmeng@andy tmp]$ bash 3-test-number.sh
                    The test value 10 is greater than 5
                    The values are different
                [jicanmeng@andy tmp]$ 

但是使用test命令测试数值有个限制:不能比较浮点值。必须要记住这一点。例如下面的例子:

[jicanmeng@andy tmp]$ cat 4-test-number-float.sh
                        #!/bin/bash

                        var1=`echo "scale=4; 10/3" | bc`
                        echo "The test value is $var1"
                        if [ $var1 -gt 3 ]
                        then
	                          echo "The result is larger than 3"
                        fi
                    [jicanmeng@andy tmp]$ bash 4-test-number-float.sh
                        The test value is 3.3333
                        test6: line 5: [: 3.3333: integer expression expected
                    [jicanmeng@andy tmp]$ 

4.2 test命令比较字符串

使用test命令进行字符串比较时,使用如下符号:

符号 描述
str1 = str2 检查str1是否等于str2
str1 != str2 检查str1是否不等于str2
str1 < str2 检查str1是否小于str2
str1 > str2 检查str1是否大于str2
-z str1 检查str1的长度是否为0
-n str1 检查str1的长度是否不为0

4.2.1 字符串的相等和不相等条件非常简单,例如:

[jicanmeng@andy tmp]$ cat 5-test-string-equal.sh
                        #!/bin/bash

                        testuser=jicanmeng
                        if [ $testuser = $USER ]
                        then
	                          echo "welcome $testuser"
                        fi
                    [jicanmeng@andy tmp]$ bash 5-test-string-equal.sh
                        welcome jicanmeng
                    [jicanmeng@andy tmp]$ 

4.2.2 测试一个字符串是否比另外一个字符串大就繁琐很多。有两个问题需要特别注意:1.大于和小于号必须要转义,否则shell会把它们当作重定向符号而把字符串值当作文件名;2.大于小于的顺序和sort命令所采用的不同。对于第一个例子,有下面的测试程序:

[jicanmeng@andy tmp]$ cat 6-test-string-greater-than-error.sh
                        #!/bin/bash
                        #mis-using string comparsions

                        var1=baseball
                        var2=hockey

                        if [ $var1 > $var2 ]
                        then
	                          echo "$var1 is greater than $var2"
                        else
	                          echo "$var1 is less than $var2"
                        fi
                    [jicanmeng@andy tmp]$ bash 6-test-string-greater-than-error.sh
                        baseball is greater than hockey
                    [jicanmeng@andy tmp]$ ls -l hockey
                        -rw-rw-r--. 1 jicanmeng jicanmeng 0 May 11 23:09 hockey
                    [jicanmeng@andy tmp]$ 

可以看到,bash shel将>当作重定向符号,生成了一个hockey文件。程序的执行结果和我们期望的不同。为了解决这个问题,我们需要使用转义字符\。将上面的程序进行修改:

[jicanmeng@andy tmp]$ cat 7-test-string-greater-than-right.sh
                        #!/bin/bash
                        #mis-using string comparsions

                        var1=baseball
                        var2=hockey

                        if [ $var1 \> $var2 ]
                        then
	                            echo "$var1 is greater than $var2"
                        else
	                            echo "$var1 is less than $var2"
                        fi
                    [jicanmeng@andy tmp]$ bash 7-test-string-greater-than-right
                        baseball is less than hockey
                    [jicanmeng@andy tmp]$ 

可以看到,现在的执行结果是正确的。

对于第二个问题,我们也用一个例子来说明:

[jicanmeng@andy tmp]$ cat 8-test-compare-sort.sh
                        #!/bin/bash

                        var1=Testing
                        var2=testing

                        if [ $var1 \> $var2 ]
                        then
	                            echo "$var1 is greater than $var2"
                        else
	                            echo "$var1 is less than $var2"
                        fi
                    [jicanmeng@andy tmp]$ bash 8-test-compare-sort.sh
                        Testing is less than testing
                    [jicanmeng@andy tmp]$ cat test11
                        Testing
                        testing
                    [jicanmeng@andy tmp]$ sort test11
                        testing
                        Testing
                    [jicanmeng@andy tmp]$ 

test命令比较两个字符串的大小时,使用标准的ASCII顺序,根据每个字符的ASCII数值来决定派寻顺序。而sort命令使用的是系统的本地化语言设置中定义的排序顺序。对于英语,本地化设置指定了在派寻顺序中小写字母出现在大写字母前面。

注意,test命令使用标准的数学比较符号来进行字符串比较,而用文本代码来进行数值的比较。

4.2.3 -z和-n参数。

还是举例说明:

[jicanmeng@andy tmp]$ cat 9-test-string-n-z.sh
                        #!/bin/bash

                        var1=testing
                        var2=''

                        if [ -n "$var1" ]
                        then
	                            echo "The string '$var1' is not empty"
                        else
	                            echo "The string '$var1' is empty"
                        fi

                        if [ -z "$var2" ]
                        then
	                            echo "The string '$var2' is not empty"
                        else
	                            echo "The string '$var2' is empty"
                        fi

                        if [ -z "$var3" ]
                        then
	                            echo "The string '$var3' is not empty"
                        else
	                            echo "The string '$var3' is empty"
                        fi
                    [jicanmeng@andy tmp]$ bash 9-test-string-n-z.sh
                        The string 'testing' is not empty
                        The string '' is not empty
                        The string '' is not empty
                    [jicanmeng@andy tmp]$ 

变量var3没有被定义,所以$var3返回空,它的长度也是为0的。

要注意的是,用-n和-z测试的时候,后面的变量一定要使用""括起来。原因是因为不加括号有两个缺陷:1. 如果后面的变量为空,那么条件就始终成立。2. 如果后面的变量包括空格,就会提示参数过多的错误。

例如:if [ -n $var1 ], 如果以前没有定义var1,那么bash shell直接把本行解释为if [ -n ],这个条件是始终成立的。我们可以看一看手册:

在man test中,
    -n STRING
        the length of STRING is nonzero

    STRING equivalent to -n STRING

    -z STRING
        the length of STRING is zero

所以,可以看出来,[ -n ]等价于[ -n -n ],-n作为SRTING,长度不为零,所以本判断条件始终成立。

我们也可以直接在bash shell中测试即可(在没有定义var1变量的情况下):

4.3 test命令比较文件

准确的说,是测试文件的属性,而不是比较文件。例如检查某文件是否存在,是否可读,可写,文件的默认组是否与当前用户相同。下面是常见的选项:

符号 描述
-e file 检查file是否存在
-d file 检查file是否存在并是一个目录
-f file 检查file是否存在并是一个文件
-r file 检查file是否存在并可读
-w file 检查file是否存在并可写
-x file 检查file是否存在并可执行
-s file 检查file是否存在并非空
-O file 检查file是否存在并属当前用户所有
-G file 检查file是否存在并且默认组与当前用户相同
file1 -nt file2 检查file1是否比file2新
file1 -lt file2 检查file1是否比file2旧

这里需要注意的是-G选项,它只会去比较默认组。另外在使用-nt和-ot比较两个文件时,必须先确认文件存在。

5. 复合条件测试

if-then语句允许你使用布尔逻辑来组合测试。有两种布尔运算符可用:

  1. [ condition1 ] && [ condition2 ]
  2. [ condition1 ] || [ condition2 ]

举例如下:

[jicanmeng@andy tmp]$ cat 10-two-command-condition.sh
                    #!/bin/bash

                    if [ -d $HOME ] && [ -w $HOME/testing ]
                    then
	                        echo "The file exists and you can write to it"
                    else
	                        echo "I can not write to the file"
                    fi
                [jicanmeng@andy tmp]$ bash 10-two-command-condition.sh
                    I can not write to the file
                [jicanmeng@andy tmp]$ 

6. if-then的高级特性

bash shell有两项新的扩展,提供了可在if-then语句中使用的高级特性:

  1. 用于数学表达式的双圆括号()
  2. 用于高级字符串处理功能的双方括号[]

6.1 使用双圆括号

在用test命令进行数值比较时,只能进行简单的算数操作,而且还不能用数学符号。双圆括号则提供了非常多的数学符号。其格式如下:
(( expression ))
expression可以是任意的数学赋值表达式或比较表达式。可以使用下面的运算符:

符号 描述
val++ 先取值,再加
val-- 先取值,再减
++val 先加,再取值
--val 先减,再取值
! 逻辑求反
~ 位求反
** 幂运算
<< 左位移
>> 右位移
& 位与运算
| 位或运算
&& 逻辑与运算
|| 逻辑或运算

你可以在if语句中使用双圆括号命令,也可以在脚本的普通命令中使用来进行赋值。例如:

[jicanmeng@andy tmp]$ cat 11-senoir-square.sh
                    #!/bin/bash

                    var1=10

                    if (( $var1 ** 2 > 90 ))
                    then
	                        (( var2 = $var1 ** 2 ))
	                        echo "The square of $var1 is $var2"
                    fi
                [jicanmeng@andy tmp]$ bash 11-senoir-square.sh
                    The square of 10 is 100
                [jicanmeng@andy tmp]$ 

注意:1.不需要将双圆括号中表达式里的大于号转义,这是双圆括号提供的另一个高级特性; 2.双圆括号中赋值时,等号前后可以有空格。

6.2 使用双方括号

双方括号提供了针对字符串比较的高级特性。其格式如下:
[[ expression ]]
expression使用了test命令中采用的标准字符串进行比较。但它提供了test命令未提供的一个特性:模式匹配。举例如下:

[jicanmeng@andy tmp]$ cat 12-senior-bracket.sh
                    #!/bin/bash

                    if [[ $USER == jican* ]]
                    then
	                        echo "Hello $USER"
                    else
	                        echo "Sorry, i don't know you"
                    fi
                [jicanmeng@andy tmp]$ bash 12-senior-bracket.sh
                    Hello jicanmeng
                [jicanmeng@andy tmp]$ 

这里的等于比较,可以用==,也可以用=。

另外,和双圆括号一样,双方括号内大于号也不需要转义。例如:

[jicanmeng@andy tmp]$ cat 13-senior-bracket.sh
                    #!/bin/bash
                    #mis-using string comparsions

                    var1=baseball
                    var2=hockey

                    if [[ $var1 > $var2 ]]
                    then
	                        echo "$var1 is greater than $var2"
                    else
	                        echo "$var1 is less than $var2"
                    fi
                [jicanmeng@andy tmp]$ bash 13-senior-bracket.sh
                    baseball is less than hockey
                [jicanmeng@andy tmp]$ 

7. case命令

和c语言一样,bash shell也支持case命令。其格式如下:

case variable in
pattern1 | pattern2) commands1;;
pattern3) commands2;;
*) default commands;
esac

举例如下:

[jicanmeng@andy tmp]$ cat 14-case.sh
                    #!/bin/bash

                    case $USER in
                    jicanmeng | barbara)
	                        echo "Welcome, $USER";;
                    testing)
	                        echo "special testing account";;
                    *)
	                        echo "Sorry, you are not allowed here";;
                    esac
                [jicanmeng@andy tmp]$ bash 14-case.sh
                    Welcome, jicanmeng
                [jicanmeng@andy tmp]$ 

参考资料

  1. Lnux命令行与shell脚本编程大全
  2. 鸟哥的linux私房菜:
    http://linux.vbird.org/linux_basic/0340bashshell-scripts.php