Work the Shell - 条件语句和流程控制

by Dave Taylor

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 语句是无与伦比的!

总结

当然,有很多不同的方法可以创建更高级和更复杂的脚本,特别是包括 shell 脚本函数,但我们将在稍后深入探讨这些内容。我非常喜欢直接动手,而不是永远围绕这个话题空谈。

我希望本专栏中关于流程控制和条件表达式评估的基础知识已经足够了。如果您有任何问题,请不要忘记man sh提供了有关 Bourne Shell 的功能和特性的更多信息。

我不知道你怎么想,但我很渴望开始处理一些更复杂和有趣的脚本编写任务,我邀请您通过电子邮件告诉我您是否有兴趣在这里看到特定类型的脚本。

Dave Taylor 是 UNIX 领域的 25 年资深人士,Elm Mail System 的创建者,也是最近畅销书 Wicked Cool Shell ScriptsTeach Yourself Unix in 24 Hours 的作者,他的技术书籍共有 16 本。他的主要网站是 www.intuitive.com

Load Disqus comments