shell编程:条件判断,循环

使用结构化命令

使用 if-then 语句

基本的结构化命令就是 if-then 语句。if-then 语句有如下格式。

if command
then
    commands
fi

在其他编程语言 中,if 语句之后的对象是一个等式,这个等式的求值结果为 TRUE 或 FALSE。但 bash shell 的 if 语 句并不是这么做的。

bash shell 的 if 语句会运行 if 后面的那个命令。如果该命令的退出状态码是 0 (该命令成功运行),位于 then 部分的命令就会被执行。如果该命令的退出状态码是其他值, then 部分的命令就不会被执行,bash shell 会继续执行脚本中的下一个命令。fi 语句用来表示 if-then 语句到此结束。

你可能在有些脚本中看到过 if-then 语句的另一种形式:

if command; then

commands

fi

通过把分号放在待求值的命令尾部,就可以将 then 语句放在同一行上了,这样看起来更 像其他编程语言中的 if-then 语句。

在 then 部分,你可以使用不止一条命令。可以像在脚本中的其他地方一样在这里列出多条 命令。bash shell 会将这些命令当成一个块,如果 if 语句行的命令的退出状态值为 0,所有的命令 都会被执行;如果 if 语句行的命令的退出状态不为 0,所有的命令都会被跳过。

if-then-else 语句

if-then-else 语句在语句中提供了另外一组命令。

if command
then
    commands
else
    commands
fi

当 if 语句中的命令返回退出状态码 0 时,then 部分中的命令会被执行,这跟普通的 if-then 语句一样。当 if 语句中的命令返回非零退出状态码时,bash shell 会执行 else 部分中的命令。

嵌套 if

在脚本中使用这种嵌套 if-then 语句的问题在于代码不易阅读,很难理清逻辑流程。
可以使用 else 部分的另一种形式:elif。这样就不用再书写多个 if-then 语句了。elif 使 用另一个 if-then 语句延续 else 部分。

if command1
then
    commands
elif command2
then
    more commands
fi

test 命令

test 命令提供了在 if-then 语句中测试不同条件的途径。如果 test 命令中列出的条件成立, test 命令就会退出并返回退出状态码 0。这样 if-then 语句就与其他编程语言中的 if-then 语句 以类似的方式工作了。如果条件不成立,test 命令就会退出并返回非零的退出状态码,这使得 if-then 语句不会再被执行

test 命令的格式非常简单。

test condition

condition 是 test 命令要测试的一系列参数和值。

当你加入一个条件时,test 命令会测试该条件。例如,可以使用 test 命令确定变量中是否 有内容。

bash shell 提供了另一种条件测试方法,无需在 if-then 语句中声明 test 命令。

if [ condition ]
then
    commands
fi

方括号定义了测试条件。注意,第一个方括号之后和第二个方括号之前必须加上一个空格, 否则就会报错。

test 命令可以判断三类条件:

  • 数值比较
  • 字符串比较
  • 文件比较

数值比较

使用 test 命令常见的情形是对两个数值进行比较。

数值条件测试可以用在数字和变量上。

#!/bin/bash

value1=10
value2=11

if [ $value1 -gt 5]
then
    echo "The test value $value1 is greater than 5"
fi

if [ $value1 -eq $value2]
then
    echo "The values are equal"
else
    echo "The values are different"
fi

记住,bash shell 只能处理整数。如果你只是要通过 echo 语句来显示这个结果,那没问题.但是,在基于数字的函数中就不行了.

字符串比较

条件测试还允许比较字符串值。

字符串相等性

字符串的相等和不等条件不言自明,很容易看出两个字符串值是否相同。

在比较字符串的相等性时,比较测试会将所有的标点和大小写情况都考虑在内。

字符串顺序

要测试一个字符串是否比另一个字符串大就是麻烦的开始。当要开始使用测试条件的大于或 小于功能时,就会出现两个经常困扰 shell 程序员的问题:

  • 大于号和小于号必须转义,否则 shell 会把它们当作重定向符号,把字符串值当作文件 名
  • 大于和小于顺序和 sort 命令所采用的不同。
if [ $val1 \> $val2 ]

sort 命令处理大写字 母的方法刚好跟 test 命令相反。

在比较测试中,大写字母被认为是小于小写字母的。但 sort 命令恰好相反。当你将同样的 字符串放进文件中并用 sort 命令排序时,小写字母会先出现。这是由各个命令使用的排序技术 不同造成的。

比较测试中使用的是标准的 ASCII 顺序,根据每个字符的 ASCII 数值来决定排序结果。sort 命令使用的是系统的本地化语言设置中定义的排序顺序。对于英语,本地化设置指定了在排序顺 序中小写字母出现在大写字母前

test 命令和测试表达式使用标准的数学比较符号来表示字符串比较,而用文本代码来表 示数值比较。这个细微的特性被很多程序员理解反了。如果你对数值使用了数学运算符 号,shell 会将它们当成字符串值,可能无法得到正确的结果。

字符串大小

-n 和-z 可以检查一个变量是否含有数据。

if [ -n $value1 ]

空的和未初始化的变量会对 shell 脚本测试造成灾难性的影响。如果不是很确定一个变量的 内容,最好在将其用于数值或字符串比较之前先通过-n 或-z 来测试一下变量是否含有值。

文件比较

后一类比较测试很有可能是 shell 编程中为强大、也是用得多的比较形式。它允许你测 试 Linux 文件系统上文件和目录的状态。

shell编程:条件判断,循环

这些测试条件使你能够在 shell 脚本中检查文件系统中的文件。它们经常出现在需要进行文件 访问的脚本中。

检查目录

-d 测试会检查指定的目录是否存在于系统中。如果你打算将文件写入目录或是准备切换到某 个目录中,先进行测试总是件好事情。

...
if [ -d $jump_dictory ]
...

检查对象是否存在

-e 比较允许你的脚本代码在使用文件或目录前先检查它们是否存在。

...
if [ -e $location ]

...

if [ -e $location/$file_name]
...

第一次检查用-e 比较来判断用户是否有$HOME目录。如果有,接下来的-e比较会检查 sentinel文件是否存在于$HOME 目录中。

检查文件

-e 比较可用于文件和目录。要确定指定对象为文件,必须用-f 比较。

...
if [ -e $item_name ]
then
...
    if [ -f $item_name ]
    ...

检查是否可读

在尝试从文件中读取数据之前,好先测试一下文件是否可读。可以使用-r 比较测试。

...
if [ -r $pwfile ]
...

检查空文件

应该用-s 比较来检查文件是否为空,尤其是在不想删除非空文件的时候。要留心的是,当 -s 比较成功时,说明文件中有数据。

...
if [ -s $file_name ]
...

检查是否可写

-w 比较会判断你对文件是否有可写权限。

...
if [ -w $item_name ]
...

检查文件是否可执行

-x 比较是判断特定文件是否有执行权限的一个简单方法。虽然可能大多数命令用不到它,但 如果你要在 shell 脚本中运行大量脚本,它就能发挥作用。

...
if [ -x test16.sh ]
...

检查所属关系

-O 比较可以测试出你是否是文件的属主。

...
if [ -O /etc/passwd ]
...

检查默认属组关系

-G 比较会检查文件的默认组,如果它匹配了用户的默认组,则测试成功。由于-G 比较只会 检查默认组而非用户所属的所有组,这会叫人有点困惑。

#!/bin/bash

if [ -G $HOME/testing ]
then
    echo "You are in the same group as the file"
else
    echo "The file is not owned by your group"
fi

检查文件日期

后一组方法用来对两个文件的创建日期进行比较。这在编写软件安装脚本时非常有用。有 时候,你不会愿意安装一个比系统上已有文件还要旧的文件。

-nt 比较会判定一个文件是否比另一个文件新。如果文件较新,那意味着它的文件创建日 期更近。-ot 比较会判定一个文件是否比另一个文件旧。如果文件较旧,意味着它的创建日期 更早。

这些比较都不会先检查文件是否存在。

如果文件不存在,-nt 比较会返回一个错误的结果。在你尝试使用-nt 或 -ot 比较文件之前,必须先确认文件是存在的。


复合条件测试

if-then 语句允许你使用布尔逻辑来组合测试。有两种布尔运算符可用:

  • [ condition1 ] && [ condition2 ]
  • [ condition1 ] || [ condition2 ]
#!/bin/bash

if [ -d $HOME ] && [ -w $HOME/testing]
then
    echo "The file exists and you can write to it"
else
    echo "I cannot write to the file"
fi

if-then 的高级特性

bash shell 提供了两项可在 if-then 语句中使用的高级特性:

  • 用于数学表达式的双括号
  • 用于高级字符串处理功能的双方括号

使用双括号

双括号命令允许你在比较过程中使用高级数学表达式。test 命令只能在比较中使用简单的 算术操作。双括号命令提供了更多的数学符号,这些符号对于用过其他编程语言的程序员而言并不陌生。双括号命令的格式如下:

(( expression ))

expression 可以是任意的数学赋值或比较表达式。

shell编程:条件判断,循环

可以在 if 语句中用双括号命令,也可以在脚本中的普通命令里使用来赋值。

...
if (( $val1 ** 2 > 90))
then
    (( val2 = $valt1 ** 2))
    echo "The square of $val1 is $val2"
fi

注意,不需要将双括号中表达式里的大于号转义。这是双括号命令提供的另一个高级特性。

使用双方括号

双方括号命令提供了针对字符串比较的高级特性。双方括号命令的格式如下:

[[ expression ]]

双方括号里的 expression 使用了 test 命令中采用的标准字符串比较。但它提供了 test 命 令未提供的另一个特性——模式匹配(pattern matching)

双方括号在 bash shell 中工作良好。不过要小心,不是所有的 shell 都支持双方括号。

在模式匹配中,可以定义一个正则表达式来匹配字符串值。

...
if [[ $USER == r*]]
then
    echo "Hello $USER"
else
    echo "Sorry, I don‘t know you"
fi
...

在上面的脚本中,我们使用了双等号(==)。双等号将右边的字符串(r*)视为一个模式, 并应用模式匹配规则。双方括号命令$USER 环境变量进行匹配,看它是否以字母 r 开头。如果是 的话,比较通过,shell 会执行 then 部分的命令。


case 命令

你会经常发现自己在尝试计算一个变量的值,在一组可能的值中寻找特定值。

有了 case 命令,就不需要再写出所有的 elif 语句来不停地检查同一个变量的值了。case 命 令会采用列表格式来检查单个变量的多个值。

case variable in
pattern1 | pattern2) commands1;;
pattern3) commands2;;
*) default commands;;
esac

case 命令会将指定的变量与不同模式进行比较。如果变量和模式是匹配的,那么 shell 会执行 为该模式指定的命令。可以通过竖线操作符在一行中分隔出多个模式模式。星号会捕获所有与已 知模式不匹配的值。

#!/bin/bash

case $USER in
rich | barbara)
    echo "Welcome, $USER"
    echo "Please enjoy your visit";;
testing)
    echo "Special testing account";;
jessica)
    echo "Do not forget to log off when you‘re done";;
*)
    echo "Sorry, you are not allowed here";;
esac

case 命令提供了一个更清晰的方法来为变量每个可能的值指定不同的选项。

shell编程:条件判断,循环

上一篇:linux加密


下一篇:Emacs (手动)安装笔记 (Ubuntu)