Shell 脚本实战 - 探索管道、测试和流程控制

作者:Dave Taylor

上个月我们从简单的文件重定向开始。本月我将继续讨论 shell 脚本编程的基本构建块,首先探索管道,然后我们将深入研究一些基本的编程语句,以便我们可以进入一个有趣的编程项目。

许多刚开始使用 Linux 命令行的人没有意识到它与图形界面的世界不同,在图形界面中,程序都是独立的实体,彼此之间无法真正交互(也就是说,Photoshop 无法直接将输出馈送到 Microsoft Word,如果您是 Windows 用户,或者在 Linux 术语中,GIMP 无法轻松地与 OpenOffice.org 交互)。我们被教导将程序视为 автономные,但是当您在 Linux 命令行中时,程序之间可以相互通信。

这是一个真正的福音,因为这意味着您实际上拥有数百万个不同的命令,而不是大约 1,800 个不同的可用命令,这些命令可以组合在一起以完成您能想到的几乎任何事情。

关键是 |(管道)符号,它将第一个命令的输出连接到第二个命令的输入。例如,想知道您的主目录中有多少个文件吗?简单的解决方案是

ls | wc -l

它调用 ls 命令来列出文件,但实际上不是在屏幕上显示输出,而是将其馈送到 wc(字数统计)程序,-l 选项指示我们想要统计输入流中的行数,而不是单词或字符数。

现在,这是一个稍微复杂一点的例子。假设您想知道有多少文件是上次在 8 月份修改的。当您使用ls -l命令时,您会注意到这些行具有以下典型特征

drwxr-xr-x    11 taylor  taylor    374 Aug  16 21:57 ConnectSafely

这提供了很多信息,但我们只关心上次修改的月份显示为三个字母的缩写,并被空格包围。grep 命令可以轻松地仅匹配输入流中的特定模式,所以现在让我们构建一个三部分组成的管道,它列出当前目录中的所有文件,筛选掉所有不是来自 8 月份的文件,然后计算剩余的行数

ls -l | grep " Aug " | wc -l

明白它是如何工作的吗?您应该认为,即使是对标准的 20 或 30 个 Linux 命令有初步了解的人,也拥有一个强大的交互式环境,随时可以供他们调用。您是对的!(请注意,如果您的文件不够旧,您将看不到ls -l输出中的月份名称。移动到一个较旧的目录,例如 /etc,然后再次尝试该命令;很可能您会在该目录中找到足够多的旧文件。)

您甚至可以拥有一个管道,将其最终输出保存到文件中,只需在管道末尾添加重定向即可

ls -l | grep " Aug " > files.from.August

并且,通过使用鲜为人知的 tee 命令,您甚至可以在管道中间保存数据流的副本

ls -l | grep " Aug " | tee aug.output | wc -l

这里我们有与之前相同的输出,但现在中间结果的副本已整齐地保存在 aug.output 文件中。(另外一个有用的提示:您可以使用tee /dev/tty并在屏幕上显示中间输出的副本,即使它也被馈送到管道中的下一步。)

从命令行可以访问数千个 Linux 命令,并且除了极少数命令外,所有命令都可以轻松地添加到命令管道中。考虑到一个典型的命令也至少有六个不同的选项来更改其行为,您可以感受到命令行环境是多么丰富,以及为什么如此多的 Linux 高级用户和管理员仍然为了他们的大部分工作而避开 GUI。

流程控制和 test 命令

shell 脚本的下一个构建块是流程控制。这是从晦涩难懂的 APL 到现在平淡无奇的 BASIC 的任何编程语言的基本要素。幸运的是,shell 脚本程序员可以使用许多流程控制元素,从最基本的 if-then-else-fi 到更复杂的 while-do-end 和 repeat-until 块以及 switch-case-end。

要研究流程控制,有必要花几分钟时间绕道谈谈 shell 脚本程序员最重要的命令之一:test。test 命令通常是评估条件语句并确定结果是 TRUE 还是 FALSE 的程序,这显然是任何类型的条件流程控制的关键功能。

信不信由你,test 命令链接到 [ 命令,这就是为什么你可以用两种方式编写条件语句,如下例所示

if test -f filename
if [ -f filename ]

这个特定的条件测试是为了查看文件名是否确实是一个文件(-f 测试)。如果您使用更易读的 [ 表示法,则还需要包含结束 ] 符号;而如果您明确使用 test,则可以跳过条件语句上的任何结束符号。

提示:使用 [ 符号还有第二个好处。许多现代 shell 都有内置在 shell 本身中的 test 命令版本,这大大加快了 shell 脚本的执行速度。使用 [ 符号可确保您在可用时使用内置版本,但显式调用 test 意味着您在运行脚本时可能不会获得该性能提升。

test 命令至少有 30 个不同的选项,熟悉它们至关重要,这样您才能了解如何测试两个字母数字字符串(例如,文件名)与如何测试数值(文件大小),甚至执行一系列令人眼花缭乱的文件和目录测试,包括执行权限、非零大小、它是否是管道或套接字以及更多可能性的测试。要开始了解有关此命令的更多信息,请键入man test在您的终端中。

有了 test 命令,标准的 if-then 条件结构如下所示

if [ condition ]
then
        statement block if condition is true
else
        statement block if condition is false
fi

通常,您会看到程序员使用一个小的简写方式,添加一个分号,因此前两行可以写成

if [ condition ] ; then

这是一个关于如何使用它的快速示例,在我用完本期空间之前

if [ -w . ] ; then
   echo "I can write to the current directory. "
else
  echo "I cannot write to the current directory. "
fi

如您所见,这提供了一种快速测试您是否对当前目录具有写入权限的方法。将其输入到编辑器(vi 或 emacs,随便您喜欢哪个)并将其保存在您的主目录中,命名为 dir.write.sh,然后您可以使用cd移动到不同的目录,并通过键入sh ~/dir.write.sh来运行此第一个 shell 脚本,以查看您是否在该目录中具有写入权限。

空间不足。下个月,我们将花更多时间研究条件语句和流程控制,并开始思考如何编写一个基本的二十一点游戏作为 shell 脚本。下期再见!

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

加载 Disqus 评论