Shell条件语句:if
写脚本时候很多情况下我们需要求shell
脚本中的命令施加一些流程控制。类似于其他编程语言,条件语句允许我们改变命令执行的顺序,这些指令统称为结构化指令。在bash shell中有不少结构化命令,我们会逐个研究,这次讲解一下if
语句。
1. 基础
1.1 if-then语句
if
语句的最基础格式为:
if command
then
commands
fi
有的时候也写为:
if command; then
commands
fi
因为在shell
中,;
标志着一个语句的结束,所以在shell
解释器解析的时候,if command
和then
会被解析为两个语句,所以在逻辑上这两种写法是一样的。
在上面的语句中:
command
是一个要执行的命令或命令序列。它可以是任何有效的shell
命令,例如ls
、grep
、echo
等。then
关键字表示如果command
命令的退出状态(即返回值)为0(表示成功),则执行紧随其后的commands
代码块。commands
是在command
命令的退出状态为0时要执行的一系列命令或代码。你可以在这个代码块中编写任何有效的shell命令。fi
是if
语句的结束标记,用于表示条件语句的结束。
例如下面的例子:
#!/bin/env bash
if pwd; then
echo "pwd worked"
fi
运行的结果就是:
如果命令运行出错的话,就不会输出了:
#!/bin/env bash
if NotaCommand; then
echo "NotaCommand worked"
fi
这里我们使用了一个不存在的命令,所以shell
执行语句的返回值自然就不是0了,所以就不会输出文字了。
一般来说,命令根据运行的情况返回值是不同的,利用这一特性,我们就可以编写出有用的if
语句。
例如在文本中查找内容的grep
语句:
- 找到了内容则返回值为0
- 没找内容则返回值为1
所以就能编写出这样测试用户的脚本:
#!/bin/env bash
#
# 测试用户是否存在
#
testuser=jack
#
if grep $testuser /etc/passwd; then
echo "系统上存在用户${testuser}"
echo "该用户家目录为:"
ls -a /home/$testuser/
fi
1.2 if-then-else语句
有了if-then
语句的基础,if-then-else
语句就很简单了。
if command; then
commands1
else
commands2
fi
其中:
command
是一个要执行的命令或命令序列。它可以是任何有效的shell命令,例如ls
、grep
、echo
等。then
关键字表示如果command
命令的退出状态(即返回值)为0(表示成功),则执行紧随其后的commands1
代码块。commands1
是在command
命令的退出状态为0时要执行的一系列命令或代码。你可以在这个代码块中编写任何有效的shell命令。else
关键字用于指定在条件为假时要执行的代码块。commands2
是在command
命令的退出状态非0时要执行的一系列命令或代码。如果提供了else
块,那么在条件为假时将执行这个代码块。fi
是if
语句的结束标记,用于表示条件语句的结束。
1.3 if-then-elif语句
同理,if-then-elif
语句也很好理解
if command1; then
commands1
elif command2; then
commands2
fi
其中:
command1
是第一个要执行的命令或命令序列。它可以是任何有效的shell命令,例如ls
、grep
、echo
等。then
关键字表示如果command1
命令的退出状态(即返回值)为0(表示成功),则执行紧随其后的commands1
代码块。commands1
是在command1
命令的退出状态为0时要执行的一系列命令或代码。你可以在这个代码块中编写任何有效的shell命令。elif
关键字用于指定另一个条件和相应的代码块。command2
是第二个要执行的命令或命令序列。它可以是任何有效的shell命令。then
关键字表示如果command2
命令的退出状态为0,则执行紧随其后的commands2
代码块。commands2
是在command2
命令的退出状态为0时要执行的一系列命令或代码。fi
是if
语句的结束标记,用于表示条件语句的结束。
2. 条件测试
上面我们介绍到,shell
只会根据命令的返回值来判断该运行那个语句,可是很多时候我们需要的不是运行某个命令,而是进行条件测试。
例如判断某个文件是否存在,或者某个变量是否存在。尽管我们可以使用ls
或者cat
来判断文件是否存在,但是对于判断变量是否存在就难了。
幸运的是,有一个shell
为我们提供了一个命令test
,用来专门进行条件测试:
test
命令的一般形式是:
test condition
或者使用方括号的形式,两者是等价的:
[ condition ]
注意,第一个方括号之后和第二个方括号之前必须加上一个空格,否则就会报错,因为
[
和]
其实也是命令
condition
是要测试的条件表达式,可以是各种比较、逻辑和文件测试运算符的组合
test
命令和[]
可以判断三类条件:
- 数值比较
- 字符串比较
- 文件比较
2.1 数值比较
使用数值比较的时候需要使用一些参数,例如大于还是等于
比 较 | 解释 | 描述 |
---|---|---|
n1 -eq n2 |
equal | 检查n1是否与n2相等 |
n1 -ge n2 |
greater equal | 检查n1是否大于或等于n2 |
n1 -gt n2 |
greater than | 检查n1是否大于n2 |
n1 -le n2 |
less equal | 检查n1是否小于或等于n2 |
n1 -lt n2 |
less than | 检查n1是否小于n2 |
n1 -ne n2 |
non equal | 检查n1是否不等于n2 |
例如:
#!/bin/env bash
value1=10
value2=11
if [ $value1 -gt 5 ]; then
echo "The test value $value1 is greater than 5"
fi
# 使用 test 也是一样的效果
if test $value1 -eq $value2; then
echo "The values are equal"
else
echo "The values are different"
fi
2.2 字符串比较
条件测试还允许比较字符串值
比较 | 说明 | 描述 |
---|---|---|
str1 = str2 |
- | 检查str1是否和str2相同 |
str1 != str2 |
- | 检查str1是否和str2不同 |
str1 < str2 |
按照字典序 | 检查str1是否比str2小 |
str1 > str2 |
按照字典序 | 检查str1是否比str2大 |
-n str1 |
non zero | 检查str1的长度是否非0 |
-z str1 |
zero | 检查str1的长度是否为0 |
需要注意的是:
- 大于号和小于号必须转义,否则
shell
会把它们当作重定向符号,把字符串值当作文件名;
例如:
#!/bin/env bash
testuser=jack
#
if [ $USER = $testuser ]; then
echo "Welcome $testuser"
fi
使用-n
和-z
可以用来判断一个变量是否已经被初始化了,因为shell
中除了数字都被视为字符串
#!/bin/bash
val1=testing
val2=''
#
if [ -n $val1 ]; then
echo "The string '$val1' is not empty"
else
echo "The string '$val1' is empty"
fi
#
if [ -z $val2 ]; then
echo "The string '$val2' is empty"
else
echo "The string '$val2' is not empty"
fi
#
if [ -z $val3 ]; then
echo "The string '$val3' is empty"
else
echo "The string '$val3' is not empty"
fi
2.4 文件比较
文件测试很有可能是shell
编程中最为强大、也是用得最多的比较形式。它允许我们测试Linux
文件系统上文件和目录的状态。
比较 | 说明 | 描述 |
---|---|---|
-d file |
directory | 检查file是否存在并是一个目录 |
-e file |
exist | 检查file是否存在 |
-f file |
file | 检查file是否存在并是一个文件 |
-r file |
readable | 检查file是否存在并可读 |
-s file |
- | 检查file是否存在并非空 |
-w file |
writable | 检查file是否存在并可写 |
-x file |
executable | 检查file是否存在并可执行 |
-O file |
Owner | 检查file是否存在并属当前用户所有 |
-G file |
Group | 检查file是否存在并且默认组与当前用户相同 |
file1 -nt file2 |
newer than | 检查file1是否比file2新 |
file1 -ot file2 |
older than | 检查file1是否比file2旧 |
3. 复合条件测试
if
语句允许我们使用布尔逻辑来组合测试。有两种布尔运算符可用:
&&
,[ condition1 ] && [ condition2 ]
:两个条件需要同时成立||
,[ condition1 ] || [ condition2 ]
:两个条件有一个成立即可
例如:
#!/bin/env bash
#
# testing compound comparisons
#
if [ -d $HOME ] && [ -w $HOME ]; then
echo "The file exists and you can write to it"
else
echo "I cannot write to the file"
fi
4. if高级特性
如果只是介绍到这里的话这篇博客就太简单了,shell
的if
语句最强大的在于下面两个强大的高级特定:
- 用于数学表达式的双括号:
(( expression ))
- 用于高级字符串处理功能的双方括号:
`[[ expression ]]
4.1 双括号
双括号命令允许你在比较过程中使用高级数学表达式。test命令只能在比较中使用简单的算术操作。而双括号命令提供了更多的数学符号,这些符号和其他语言中的符号是一样的。
双括号命令的格式如下:
(( expression ))
expression
可以是任意的数学赋值或比较表达式。除了test命令使用的数学比较外,还支持:
符号 | 说明 | ||
---|---|---|---|
val++ |
后增 | ||
val-- |
后减 | ||
++val |
先增 | ||
--val |
先减 | ||
! |
取反 | ||
~ |
位求反 | ||
** |
幂 | ||
<< |
向左位移 | ||
>> |
向右位移 | ||
& |
位与 | ||
` | ` | 位或 | |
&& |
逻辑与 | ||
` | ` | 逻辑或 |
注意:
- 不需要将双括号中表达式里的大于号转义。这是双括号命令提供的另一个高级特性
- 双括号中变量不需要使用
$
提取值 - 可以直接声明新的变量并赋值
例如:
#!/bin/env bash
val1=10
#
if ((val1 ** 2 > 90)); then
((val2 = val1 ** 2))
echo "The square of $val1 is $val2"
fi
4.2 双方括号
双方括号命令提供了针对字符串比较的高级特性。双方括号命令的格式如下:
[[ expression ]]
双方括号里的expression
除了可以使用test
命令中采用的字符串比较,还可以进行模式匹配(pattern matching)。
即双方括号支持字符串比较运算符和模式匹配运算符,并返回一个布尔值结果。下面是一些常见的用法:
- 字符串比较:
==
:等于!=
:不等于>
:大于(按字典顺序比较)<
:小于(按字典顺序比较)-z
:空字符串
- 模式匹配:
*
:匹配任意字符序列?
:匹配任意单个字符[]
:匹配指定范围内的字符!
:取反匹配
#!/bin/env bash
# 定义一个字符串变量
str="Hello, World!"
# 字符串比较
if [[ $str == "Hello" ]]; then
echo "字符串匹配成功"
else
echo "字符串匹配失败"
fi
# 模式匹配
if [[ $str == H* ]]; then
echo "以H开头的字符串"
fi
总而言之,(())
和[[ ]]
是Shell脚本中用于数值比较、数值运算、字符串比较和模式匹配的特殊结构。它们提供了更灵活和方便的方式来处理数值和字符串的操作和判断。