|
|
|
@ -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
|