Work the Shell - 条件语句和流程控制
shell 脚本的基本构建块的最后一部分是条件语句,它允许您以编程方式决定是否基于逻辑测试执行语句块,以及流程控制语句,这是编程早期的伟大创新,您可以在其中多次执行代码块。在本专栏中,我们将探讨这两者,最后,我们将完成脚本编写的众所周知的乐高积木,从而使我们能够开始探索如何通过简单语句的新颖而独特的组合来解决复杂的脚本编写问题。
最明显的条件语句是 if-then-else,在 shell 脚本中看起来像
if condition ; then ; statements ; else ; statements2 ; fi
当然,您通常会在多行上看到它,因此它更可能看起来像这样
if condition; then statements else statements2 fi
对此有一些变体,包括安全地省略任何类型的 else 子句,但更有趣的是,您可以使用 else if 结构将条件“链接”在一起
if condition; then statements elif condition2 ; then statements2 fi
这完全有效,值得注意的是,在功能上与以下结构不同
if condition then statements if condition2; then statements2 fi fi
对于任何以前编写过程序的人来说,这种差异将是显而易见的。在第一个示例中,如果 condition 为 false 且 condition2 为 true,则将执行 statements2。然而,在后一个示例中,仅当 condition 为 true 且 condition2 为 true 时,才会执行 statements2。微妙,但非常重要!
特定的逻辑条件可以呈现多种外观,因为条件表达式的唯一要求是,如果评估的条件为假,则返回零,如果应将其视为真,则返回非零值。实际上,Linux 中有称为 false 和 true 的命令,因此您可以使用诸如“if true; then....”之类的语句。然而,大多数条件都是围绕着宝贵的 test 命令构建的,它有许多不同的标志和选项。
想要比较两个字符串(文本)值?您可以使用
if test $myvar = "exit" ; then
或其快捷方式替代方案
if [ $myvar = "exit" ] ; then
比较两个数值与
if test $numval -lt 10 ; then
test 命令中还提供了大量文件和变量测试,包括 -r 用于测试文件是否可读,-e 用于查看文件是否存在,-s 用于查看文件是否存在且具有非零大小,以及 -d 用于测试目录,-f 用于测试常规文件。
因此,如果您想区分 $filename 是文件、目录还是其他文件类型,您可以使用如下语句序列
if test -f $filename ; then echo "$filename is a regular file" elif test -d $filename ; then echo "$filename is a directory" else echo "$filename is neither a file nor a directory." fi
查看 test 手册页(使用man test)以了解您可以在 shell 脚本中使用的所有不同条件。
幸运的是,除了简单的 if-then-else 条件之外,还有许多不同的循环和流程控制结构,以下是三大结构
for x in y; do; statements; done
while x; do; statements; done
case x in ; condition1) statements ;; condition2) statements ;; esac
还有更多的条件语句,但您会发现,在绝大多数情况下,即使是最复杂的脚本,for 循环、while 循环、case 语句和 if-then-else 语句也将充当构建块。
for 循环在其变体中特别有用。想要逐步执行提供给 shell 脚本本身的参数?使用类似这样的东西
for value ; do ; statements ; done
想要逐步执行给定模式的一组匹配文件名?以下是在脚本中执行此操作的方法
for filename in *.c ; do statements done
让我们看看如何以有用的方式组合其中的几个,而不是仅仅复制手册页。这是一个简单的脚本,它检查当前目录中的每个条目,指示它是文件还是目录
for name in * do if [ -f "$name" ] ; then echo "$name is a file" elif [ -d "$name" ] ; then echo "$name is a directory" else echo "$name is neither a file nor directory" fi done
为了说明目的,让我们尝试另一个版本的脚本,一个将 *.c 识别为 C 源文件,*.h 识别为包含的头文件,*.o 识别为中间目标文件的脚本,但这次我们将使用 case 语句
for name in * do case "$name" in *.c ) echo "$name is a C source file" ;; *.h ) echo "$name is a header file" ;; *.o ) echo "$name is an object file" ;; esac done
从可读性的角度来看,case 语句是无与伦比的!
Dave Taylor 是 UNIX 领域的 25 年资深人士,Elm Mail System 的创建者,也是最近畅销书 Wicked Cool Shell Scripts 和 Teach Yourself Unix in 24 Hours 的作者,他的技术书籍共有 16 本。他的主要网站是 www.intuitive.com。