Shell脚本进阶:条件语句


Shell条件语句

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 commandthen会被解析为两个语句,所以在逻辑上这两种写法是一样的。

在上面的语句中:

  1. command是一个要执行的命令或命令序列。它可以是任何有效的shell命令,例如lsgrepecho等。
  2. then关键字表示如果command命令的退出状态(即返回值)为0(表示成功),则执行紧随其后的commands代码块。
  3. commands是在command命令的退出状态为0时要执行的一系列命令或代码。你可以在这个代码块中编写任何有效的shell命令。
  4. fiif语句的结束标记,用于表示条件语句的结束。

例如下面的例子:

#!/bin/env bash

if pwd; then
    echo "pwd worked"
fi

运行的结果就是:

if语句运行结果

如果命令运行出错的话,就不会输出了:

#!/bin/env bash

if NotaCommand; then
    echo "NotaCommand worked"
fi

if运行结果

这里我们使用了一个不存在的命令,所以shell执行语句的返回值自然就不是0了,所以就不会输出文字了。

一般来说,命令根据运行的情况返回值是不同的,利用这一特性,我们就可以编写出有用的if语句。

例如在文本中查找内容的grep语句:

  • 找到了内容则返回值为0
  • 没找内容则返回值为1

grep根据是否查找到内容返回不同的返回值

所以就能编写出这样测试用户的脚本:

#!/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

其中:

  1. command是一个要执行的命令或命令序列。它可以是任何有效的shell命令,例如lsgrepecho等。
  2. then关键字表示如果command命令的退出状态(即返回值)为0(表示成功),则执行紧随其后的commands1代码块。
  3. commands1是在command命令的退出状态为0时要执行的一系列命令或代码。你可以在这个代码块中编写任何有效的shell命令。
  4. else关键字用于指定在条件为假时要执行的代码块。
  5. commands2是在command命令的退出状态非0时要执行的一系列命令或代码。如果提供了else块,那么在条件为假时将执行这个代码块。
  6. fiif语句的结束标记,用于表示条件语句的结束。

1.3 if-then-elif语句

同理,if-then-elif语句也很好理解

if command1; then
    commands1
elif command2; then
    commands2
fi

其中:

  1. command1是第一个要执行的命令或命令序列。它可以是任何有效的shell命令,例如lsgrepecho等。
  2. then关键字表示如果command1命令的退出状态(即返回值)为0(表示成功),则执行紧随其后的commands1代码块。
  3. commands1是在command1命令的退出状态为0时要执行的一系列命令或代码。你可以在这个代码块中编写任何有效的shell命令。
  4. elif关键字用于指定另一个条件和相应的代码块。
  5. command2是第二个要执行的命令或命令序列。它可以是任何有效的shell命令。
  6. then关键字表示如果command2命令的退出状态为0,则执行紧随其后的commands2代码块。
  7. commands2是在command2命令的退出状态为0时要执行的一系列命令或代码。
  8. fiif语句的结束标记,用于表示条件语句的结束。

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高级特性

如果只是介绍到这里的话这篇博客就太简单了,shellif语句最强大的在于下面两个强大的高级特定:

  • 用于数学表达式的双括号:(( 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脚本中用于数值比较、数值运算、字符串比较和模式匹配的特殊结构。它们提供了更灵活和方便的方式来处理数值和字符串的操作和判断。


文章作者: Jack Wang
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Jack Wang !
  目录