分支

分支语句 属于 Bash

{other: "%{count} 个练习"}

关于 分支语句

Bash 中的真值

Bash 没有布尔值的概念。 它只有字符串和数字,以及由字符串或数字组成的数组。 那么,条件命令是如何处理 true 和 false 的呢?

退出状态

命令结束时会产生一个_退出状态_。 退出状态是一个 0 到 255 之间(含两端)的整数。 Bash 认为退出状态为零表示_成功_。 其他任何退出状态都表示_失败_。

这一点适用于所有命令,包括 Bash 的内置命令、关键字和函数。

“if”命令

if命令的基本语法是

if CONDITIONAL_COMMANDS; then TRUE_COMMANDS; else FALSE_COMMANDS; fi

它以if开头,条件命令与“成功”命令之间用then分隔,最后以fi结束。 else子句是可选的。

CONDITIONAL_COMMANDS 可以是一条命令,也可以是一个命令列表。

CONDITIONAL_COMMANDS 会被执行,然后

  • 如果退出状态为零(成功),就执行 TRUE_COMMANDS。
  • 如果退出状态非零(失败),并且存在 FALSE_COMMANDS,就执行它们。

可以用elif给出级联分支

if CONDITIONAL_COMMANDS
then TRUE_COMMANDS
elif CONDITIONAL_COMMANDS_2
then TRUE_COMMANDS_2
# more elif branches ...
else FALSE_COMMANDS
fi

在then、elif、else和fi这些词之前,必须有分号或换行。

需要强调的是:控制流程的是条件命令的退出状态。 举个例子,grep在找到匹配时返回 0,没找到时返回 1。 -q选项会抑制输出,只产生退出状态。

if grep -q "my pattern" my_file; then echo "the pattern is found in the file"; fi

“[”和“test”命令

CONDITIONAL_COMMANDS 周围并没有什么特殊语法。 你可能见过像下面这样的 if 语句:

if [ "$password" = "secure" ]; then
    echo "Welcome!"
fi

[并不是特殊语法。 它是一个_命令_,会计算条件表达式,并以成功或失败的状态退出。 和所有命令一样,它与实参之间必须有空白字符。

[其实是test命令的同义词。 两者完全相同,只不过[的最后一个实参_必须_是]。

条件表达式

在[和]之间,你要写一个条件表达式。 一些典型的条件表达式包括:

[ -f "$filename" ]             # file operations
[ "$string1" = "$string2" ]    # string comparisons
[ "$num1" -eq "$num2" ]        # arithmetic comparisons

还有更多可用的运算,它们列在手册的 Bash 条件表达式 一节中。

Note

在上面的例子中,注意所有变量都加了引号。 test和[命令是普通命令,它们的实参和其他命令一样,会发生单词拆分和文件名展开。

这一点值得强调,因为条件表达式会根据你提供的_实参个数_而有不同的求值方式:

  • 0 个实参:退出状态非零(失败)
  • 1 个实参:如果该实参不为空,退出状态为零(成功);如果为空,则非零。
  • 2 个实参:第一个实参必须是一元运算符(例如[ -z "$name" ]),或者是一个!(对 1 个实参测试的结果取反)
  • 以及更多情况。

如果忘了加引号,可能会得到意想不到的结果:

str=""

# this prints "empty"
if [ -n "$str" ]; then echo "not empty"; else echo "empty"; fi

# leaving the variable unquoted results in incorrect "not empty" output
if [ -n $str ]; then echo "not empty"; else echo "empty"; fi

“[["关键字

[[...]]条件结构不是_命令_,而是一个关键字。 这意味着,虽然它的处理方式和其他命令一样,但它有特殊的解析规则。 [[的特殊之处在于,其中展开的变量不会发生单词拆分或文件名展开。 也就是说,即使不加引号,这条命令的行为也符合你的预期。

if [[ -n $str ]]; then echo "not empty"; else echo "empty"; fi

[[支持test和[能处理的所有条件表达式。 此外,[[还提供

  • =~正则表达式匹配运算符,
  • ==和!=作为通配模式匹配运算符使用,
  • &&和||作为逻辑运算符(特殊解析规则),
  • <和>作为“裸”的字符串比较运算符(特殊解析规则:因为它们也是重定向符号,所以在[中必须转义)。

人们普遍认为,这些特殊功能带来的好处很大,所以应该只使用[[。 (例如 Google Shell Style Guide。)

“case”命令

case是另一种控制流命令。 它类似于其他语言中的“switch”语句。

case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac

WORD 会依次与每个 PATTERN 匹配。 一旦有模式匹配上,就会执行对应的 COMMANDS。

read -p "Guess the secret word: " word
case "$word" in
    secret) echo "Yes, you guessed it!" ;;
    ??????) echo "That's the right number of letters." ;;
    s*)     echo "You guessed the first letter." ;;
    *)      echo "Not even close! Try again." ;;
esac

每个 COMMANDS 子句都必须以两个分号;;结尾。

Note
  1. 还有一些替代写法,可以让命令继续向下贯穿执行,也可以让模式匹配继续进行。 详情请查看手册。
  2. 回想一下,我们在引用概念的文件名展开一节中讲过模式。
通过 GitHub 编辑 该链接会在新窗口或标签页中打开