主页 实验环境 学习Linux - Study Area CN

条件判断语句

在前几章,我们学习了 Shell Script 的基本内容与编辑。那么,如果想要在根据不同情况做一些事情,例如在不同发行版上干不同的事情,或者根据不同参数执行不同的命令,那么就需要用到条件判断语句了。

如果你学习过其它任何的编程语言,那么条件判断语句对你来说应该不会陌生。但是,Shell Script 的条件判断语句的语法十分特别,但是功能都是相似的。

条件判断

方括号 []

在 Shell Script 中,最常用的条件判断语句就是方括号语句了。方括号语句的语法如下:

[ 运算符 变量 ] # 一个个变量的运算
[ 变量1 运算符 变量2 ] # 两个变量的运算

方括号语句的语法很简单,就是方括号内写上你的条件判断语句,然后在外面写上 [] 即可。

方括号其实和 test 命令的功能一模一样,但是方括号可以使代码可读性更高。

注意:方括号语句的语法要求方括号内必须有空格,否则会报错。

方括号判断的结果就是命令的返回值,也就是 $?,如果返回值为 0,则表示条件判断为真,否则表示条件判断为假,可以使用 echo $? 查看。

方括号语句内可以写各种运算符:

数值比较

运算符含义符号结果为真例子结果为假例子
-eq等于=[ 1 -eq 1 ][ 1 -eq 2 ]
-ne不等于!=[ 1 -ne 2 ][ 1 -ne 1 ]
-gt大于>[ 2 -gt 1 ][ 1 -gt 1 ]
-lt小于<[ 1 -lt 2 ][ 2 -lt 1 ]
-ge大于等于>=[ 1 -ge 1 ][ 1 -ge 2 ]
-le小于等于<=[ 2 -le 2 ][ 2 -le 1 ]

这里的等于和不等于只能用数字,不能使用字符串

字符串比较

运算符含义结果为真例子结果为假例子
==等于[ a == a ][ a == b ]
!=不等于[ a != b ][ 1 != 1 ]
-n字符串长度 > 0[ -n abc ][ -n '' ]
-z字符串长度 = 0[ -z "" ][ -z abc ]

这里等于后面可以是字符串,也可以是数字,因为数字也会被变成字符串。但是比较数字还是建议使用明确的 -eq

在字符串比较中,如果传入了一个带空格的字符串,例如 "hello world",那么方括号语句会认为这是一个错误,因为方括号语句会认为这是前后两个字符串。所以,引用变量最好使用双引号包裹,例如 [ "$var" == "hello world" ]

文件判断

下文假设当前目录下有一个 file1 文件和一个 dir1 目录

运算符含义结果为真例子结果为假例子
-e存在文件或目录[ -e file1 ][ -e file2 ]
-f存在指定的文件[ -f file1 ][ -f dir1 ]
-d存在指定的目录[ -d dir1 ][ -d file1 ]

逻辑运算符

下面大括号内的内容仅方便理解,不是 Bash 命令。

运算符含义结果为真例子结果为假例子
-a[ 1 -eq 1 -a 2 -eq 2 ] { (1==1) and (2==2) }[ 1 -eq 1 -a 2 -eq 1 ] { (1==1) and (2==1) }
-o[ 1 -eq 1 -o 2 -eq 1 ] { (1==1) or (2==1) }[ 1 -eq 2 -o 2 -eq 1 ] { (1==2) or (2==1) }
![ ! 1 -eq 2 ][ ! 1 -eq 1 ]

就像与运算的示例一样,你可以在一个方括号内写多个表达式。你只需要记住,-a-o 会被最后计算,而且 -a 的优先级比 -o 高,也就是会先算 -a

双方括号 [[]]

双方括号被大多数 Shell 支持(例如在部分系统默认的 sh 中就不支持),用起来比方括号更加方便。但是,在简单判断时,出于兼容性考虑更推荐单方括号。

双方括号的语法和单方括号没有什么区别:

[[ 运算符 变量 ]] # 一个个变量的运算
[[ 变量1 运算符 变量2 ]] # 两个变量的运算

双方括号内可以直接使用上面的所有运算符,也可以使用 > 等数值比较运算符(注意:大于等于不支持)和 &&(与)、||(或)等逻辑运算符。

另外,在双方括号中,我们可以使用小括号来指定运算的顺序,就像在数学中一样(在 Bash 中不允许嵌套)。小括号和中括号一样需要加空格。例如:

[[ ( 2 > 1 ) || ( 1 > 2 ) && ( 1 > 1 ) ]]
# 读者可以尝试一下在不使用 Bash 的情况下猜猜运行结果

在双方括号中不需要和单方括号一样,引用字符串变量使用 "" 包裹。例如之前的例子:

[ "$var" == "hello world" ]
# 等价于
[[ $var == "hello world" ]]

if 语句

if 语句的语法十分简单:

if 条件语句; then
    # 你的任何代码
fi

例如:

# 输入密码到 var 变量中
read -s -p "请输入你的密码:" var

# 判断密码是否是 "password"
if [ $var == "password" ]; then
    echo "密码正确"
fi

在条件语句中,可以写 truefalse,可以直接表示真或者假。也可以写任何命令行,只要程序的返回值为 0 就表示真,非 0 就表示假。这几种参数都不需要在方括号中。

还可以有 elifelse 语句:

if 条件语句1; then
    # 你的任何代码
elif 条件语句2; then
    # 当上面的条件语句都为假,
    # 并且当前条件语句为真时
    # 执行这里的代码
    # elif 可以有多个,不是必须的
else
    # 当上面所有条件语句为假时
    # 执行这里的代码
    # else 也不是必须的
fi

例如:

# 输入用户名到 var 变量中
read -p "请输入你的用户名:" var
# 判断用户是否是 "user1"
if [ $var == "user1" ]; then
    echo "User1 你好"
# 判断用户是否是 "user2"
elif [ $var == "user2" ]; then
    echo "User2 你好"
# 判断用户是否是 "user3"
elif [ $var == "user3" ]; then
    echo "User3 你好"
# 如果都不是
else
    echo "找不到该用户"
fi

if 语句的“可玩性”非常高,你已经可以写出很多有趣的脚本了。我们下面还会学习循环和函数,这样你就可以写出更加复杂的脚本了。

case

case 语句和 if 语句类似。但是,如果你有几十条内容,那么 if 就显得不太“优雅”并且难以维护了。

一般来说,3 项以上的 if 就应该写成 case 形式了。caseif 更容易阅读和维护。

下面是 case 的语法: (感觉很奇怪)

case 变量 in
    值1)
        # 当变量等于值1时
        # 执行这里的代码
        ;;
    值2)
        # 当变量等于值2时
        # 执行这里的代码
        ;;
    *)
        # 当变量等于其他值时
        # 执行这里的代码
        # 不是必须的
        ;;
esac

注意:每一行最后的双分号 ;; 是必须的,表示结束当前分支。

如果你比较细心,会发现 esac 就是 case 倒过来写,可以用这个来记忆。前面的 if 也是一个道理。

例如上面判断用户名的代码就可以改写成:

read -p "请输入你的用户名:" var

case $var in
    "user1")
        echo "User1 你好"
        ;;
    "user2")
        echo "User2 你好"
        ;;
    "user3")
        echo "User3 你好"
        ;;
    *)
        echo "找不到该用户"
        ;;
esac

无论是 if 还是 case,还是后面要学习的循环语句,之间都是可以被随意嵌套的。例如:

read -p "请输入一个数字:" var
if [ $var -gt 5 ]; then
    echo "数字大于 5"
    if [ $var -gt 10 ]; then
        echo "数字大于 10"
    fi
else
    echo "数字小于或等于 5"
fi

&&||

&&|| 是 Shell 中的逻辑运算符,用于连接多个条件。

  • &&:表示“与”关系,只有当它两边的条件都为真时,整个表达式才为真。
  • ||:表示“或”关系,只要它两边的条件有一个为真,整个表达式就为真。

例如:

read -p "请输入你的用户名:" var
read -s -p "请输入你的密码:" var2

if [ $var == "user" ] && [ $var2 == "password" ]; then
    echo "登录成功"
else
    echo "登录失败"
fi

这两个符号遵循一种叫做“短路”的规则:

&& 符号当前一条命令为假时,后面的命令就不会执行了。

|| 符号当前一条命令为真时,后面的命令同样不会执行。

“短路”的“可玩性”其实很高,更多的用法就等待读者自行探索了。另外,这两个符号可以像分号一样连接两个命令(遵循“短路”的规则),例如 echo 1&&echo 2

课后作业

这节课的学习压力稍微大一些,但是用处比较大,对于后面的学习有一定帮助。为了更好掌握这个知识点,您可以尝试完成以下作业:

  1. 编写一个脚本,判断用户输入的数字是否大于 10。

  2. 经典问题:判断一个年份是不是闰年。

    提示:可以使用 [ $[ 数字 % 4 ] == 0 ] 来判断一个数是否能被 4 整除。

    闰年的定义是:能被 4 整除但不能被 100 整除,或者能被 400 整除的年份。

    读者可以尝试使用 if 嵌套,也可以使用 && || 来实现。

  3. 模拟一个软件的安装。复制当前目录下的 some_software/opt/some_software,但是当 /opt/some_software 存在时,请先删除这个目录。

  4. 为上一题的软件编写一个卸载脚本,如果找不到这个目录就显示 软件未安装,否则提示用户确认后然删除这个目录。


study-area-cn