diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 7aaa4ad..ac95395 100755 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -25,7 +25,7 @@ - [Job]() - [Vim编辑器](./shell_and_shell_script/vim_editor.md) - [Shell Script基本语法](./shell_and_shell_script/shell_script_basic.md) - - [条件判断语句]() + - [条件判断语句](./shell_and_shell_script/conditional_judgment.md) - [循环]() - [函数]() - [at与corntab]() diff --git a/src/shell_and_shell_script/conditional_judgment.md b/src/shell_and_shell_script/conditional_judgment.md new file mode 100644 index 0000000..4d08ca5 --- /dev/null +++ b/src/shell_and_shell_script/conditional_judgment.md @@ -0,0 +1,279 @@ +# 条件判断语句 + +在前几章,我们学习了 Shell Script 的基本内容与编辑。那么,如果想要在根据不同情况做一些事情,例如在不同发行版上干不同的事情,或者根据不同参数执行不同的命令,那么就需要用到条件判断语句了。 + +> 如果你学习过其它任何的编程语言,那么条件判断语句对你来说应该不会陌生。但是,Shell Script 的条件判断语句的语法十分特别,但是功能都是相似的。 + +## 条件判断 + +### 方括号 `[]` + +在 Shell Script 中,最常用的条件判断语句就是方括号语句了。方括号语句的语法如下: + +```bash +[ 运算符 变量 ] # 一个个变量的运算 +[ 变量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` 中就不支持),用起来比方括号更加方便。但是,在简单判断时,出于兼容性考虑更推荐单方括号。 + +双方括号的语法和单方括号没有什么区别: + +```bash +[[ 运算符 变量 ]] # 一个个变量的运算 +[[ 变量1 运算符 变量2 ]] # 两个变量的运算 +``` + +双方括号内可以直接使用上面的所有运算符,也可以使用 `>` 等数值比较运算符(注意:大于等于不支持)和 `&&`(与)、`||`(或)等逻辑运算符。 + +另外,在双方括号中,我们可以使用小括号来指定运算的顺序,就像在数学中一样(在 Bash 中不允许嵌套)。小括号和中括号一样需要加空格。例如: + +```bash +[[ ( 2 > 1 ) || ( 1 > 2 ) && ( 1 > 1 ) ]] +# 读者可以尝试一下在不使用 Bash 的情况下猜猜运行结果 +``` + +在双方括号中不需要和单方括号一样,引用字符串变量使用 `""` 包裹。例如之前的例子: + +```bash +[ "$var" == "hello world" ] +# 等价于 +[[ $var == "hello world" ]] +``` + +## `if` 语句 + +`if` 语句的语法十分简单: + +```bash +if 条件语句; then + # 你的任何代码 +fi +``` + +例如: + +```bash +# 输入密码到 var 变量中 +read -s -p "请输入你的密码:" var + +# 判断密码是否是 "password" +if [ $var == "password" ]; then + echo "密码正确" +fi +``` + +> 在条件语句中,可以写 `true` 和 `false`,可以直接表示真或者假。也可以写任何命令行,只要程序的返回值为 0 就表示真,非 0 就表示假。这几种参数都不需要在方括号中。 + +还可以有 `elif` 和 `else` 语句: + +```bash +if 条件语句1; then + # 你的任何代码 +elif 条件语句2; then + # 当上面的条件语句都为假, + # 并且当前条件语句为真时 + # 执行这里的代码 + # elif 可以有多个,不是必须的 +else + # 当上面所有条件语句为假时 + # 执行这里的代码 + # else 也不是必须的 +fi +``` + +例如: + +```bash +# 输入用户名到 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` 形式了。`case` 比 `if` 更容易阅读和维护。 + +下面是 `case` 的语法: ~~(感觉很奇怪)~~ + +```bash +case 变量 in + 值1) + # 当变量等于值1时 + # 执行这里的代码 + ;; + 值2) + # 当变量等于值2时 + # 执行这里的代码 + ;; + *) + # 当变量等于其他值时 + # 执行这里的代码 + # 不是必须的 + ;; +esac +``` + +> 注意:每一行最后的双分号 `;;` 是必须的,表示结束当前分支。 +> +> 如果你比较细心,会发现 `esac` 就是 `case` 倒过来写,可以用这个来记忆。前面的 `if` 也是一个道理。 + +例如上面判断用户名的代码就可以改写成: + +```bash +read -p "请输入你的用户名:" var + +case $var in + "user1") + echo "User1 你好" + ;; + "user2") + echo "User2 你好" + ;; + "user3") + echo "User3 你好" + ;; + *) + echo "找不到该用户" + ;; +esac +``` + +无论是 `if` 还是 `case`,还是后面要学习的循环语句,之间都是可以被随意嵌套的。例如: + +```bash +read -p "请输入一个数字:" var +if [ $var -gt 5 ]; then + echo "数字大于 5" + if [ $var -gt 10 ]; then + echo "数字大于 10" + fi +else + echo "数字小于或等于 5" +fi +``` + +## `&&` 和 `||` + +`&&` 和 `||` 是 Shell 中的逻辑运算符,用于连接多个条件。 + +- `&&`:表示“与”关系,只有当它两边的条件都为真时,整个表达式才为真。 +- `||`:表示“或”关系,只要它两边的条件有一个为真,整个表达式就为真。 + +例如: + +```bash +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