文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>编写 Linux 实用程序的艺术(二)

编写 Linux 实用程序的艺术(二)

时间:2007-02-17  来源:PHP爱好者


通用化

尝试将任务看作与您实际执行的任务类似;如果您能找出这些任务的通用描述,那么最好尝试编写一个符合该描述的实用程序。例如,如果您发现自己一天在根据词法对文本排序,而另一天在根据数字对文本排序,那么考虑编写一个通用排序实用程序也许是有意义的。

对功能进行通用化有时会导致您发现:某个看起来似乎像单个实用程序的程序,实际上却是配合起来使用的两个实用程序。这很好。编写两个设计良好的实用程序可能要比编写一个丑陋的或复杂的实用程序更容易。

做好一件事情并不意味着 仅仅 做一件事情。它意味着处理一致但有用的问题空间。许多人都使用 grep。然而,它的大量效用在于执行相关任务的能力。grep 的各种选项完成许多小实用程序的工作,如果这些工作都由单独的小实用程序来完成,最终会造成大量共享的、重复的代码。

这条规则,以及做好一件事情的规则,都是一个根本原理的必然结果:无论何时都要尽可能避免代码重复。如果您编写半打程序,其中每个都对行排序,您最终可能必须六次修复六个类似的 bug,而不是去使用一个得到更好维护的 sort 程序。

这是编写实用程序的一部分,即把大多数工作添加到完成该实用程序的过程中。您也许没有时间在最初就完全通用化一个实用程序,但是当您一直使用该实用程序就会获得相应的回报。

有时,向某个程序添加相关功能是很有用的,即使这个功能并不是用来完成完全相同的任务。例如,当运行在终端设备上时,对原始二进制数据进行完美打印的程序可能更为有用,因为它使终端进入原始模式。这样使得测试涉及键盘映射、新键盘等的问题变得容易多了。不确定为什么当您按 delete 键时却得到代字号(~)吗? 这是弄清实际发送了什么内容的容易途径。这并不是完全相同的任务,但它足够类似,因而可能成为一个附加特性。

清单 2 中的 errno 实用程序就是通用化的很好例子,因为它同时支持数字和符号名称。

健壮

实用程序的稳定性是很重要的。容易崩溃或无法处理真实数据的实用程序不是有用的实用程序。实用程序应该能够处理任意长度的行、巨型文件,等等。实用程序无法处理超过其内存容量的数据集或许是可以容忍的,但是有些实用程序不是这样;例如,sort 通过使用临时文件,一般能够对比其内存容量大得多的数据集排序。

应该尽量确保弄清楚您的实用程序可能要操作哪些数据。不要简单地忽略无法处理的数据的可能性。应该检查这种情况并诊断您的实用程序。错误消息越明确,您对用户就越有帮助。尽量给用户提供足够的信息,以便让他们知道发生了什么情况以及如何解决。当处理数据文件时,尽量准确识别出不良的数据。当尝试解析数字时,不要简单地放弃;应该告诉用户您得到了什么数据,而且如果可能的话,还应该告诉用户该数据位于输入流中的哪一行上。

作为一个很好的例子,请考虑 dc 的两种实现之间的区别。如果您运行 dc /home ,其中一种实现会显示“Cannot use directory as input!”而另一种实现只是无声地返回,没有错误消息,也没有不寻常的退出代码。当您错误地键入一个 cd 命令时,您更希望当前路径中有哪一种实现呢?类似地,如果您提供某个目录中的数据流(或许是执行 dc </home),前者会给出详细的错误消息。另一方面,当它在获得无效数据的早期就选择放弃可能是理想的。

安全漏洞经常植根于在意料之外的数据面前表现得不够健壮的程序中。务必记住,优秀的实用程序能够设法在 shell 脚本中作为根(root)用户身份运行。诸如 find 这样的程序中的缓冲区溢出可能会给大量的系统带来风险。

程序对意料之外的数据处理得越好,它就更可能适应变化的环境。通常,设法使程序更健壮会导致您更好地理解该程序的作用,从而更好地使之通用化。

新颖

要编写的最糟糕的实用程序种类之一就是您已经有了的实用程序。我编写过一个名为 count 的美妙的实用程序。它允许我执行几乎任何计数任务。它是一个出色的实用程序,但是已经有一个名为 jot 的标准 BSD 实用程序做同样的事情。同样地,我的一个用于将数据转换为列的灵活的程序重复了一个现有实用程序 rs 的功能,这个实用程序同样可以在 BSD 系统上找到,只不过 rs 更灵活,设计得更好。请参阅下面的 参考资料 以了解关于 jot 和 rs 的更多信息。

如果您即将开始编写一个实用程序,请花一点时间浏览一下各种系统,以确定那样的实用程序是否已经存在。不要害怕在 BSD 上借用 Linux 实用程序,或在 Linux 上借用 BSD 实用程序;实用程序代码的乐趣之一在于,几乎所有实用程序都具有很好的可移植性。

不要忘了考察一下组合现有应用程序来形成一个实用程序的可能性。从理论上讲,组合现有程序来形成的实用程序运行得不足够快是可能的,但是编写一个新的实用程序很少会比等待一个稍慢的管道更快。

一个例子实用程序

从某种意义上,这个程序是一个可执行文件,因为对于作为过滤器来说,它决不会有任何用处。然而,它作为一个命令行实用程序却工作得非常好。

这个程序仅做一件事情。它以近乎完美的输出格式输出 /usr/include/sys/errno.h 中的 errno 行。例如:

$ errno 22

EINVAL [22]: Invalid argument

清单 2. errno 查找器

#!/bin/sh
usage() {
echo >&2 'usage: errno [numbers or error names]n'
exit 1
}

for i
do
case '$i' in
[0-9]*)
awk '/^#define/ && $3 == ''$i'' {
for (i = 5; i <NF; ++i) {
foo = foo ' ' $i;
}
printf('%-22s%sn', $2 ' [' $3 ']:', foo);
foo = ''
}' </usr/include/sys/errno.h
;;
E*)
awk '/^#define/ && $2 == '''$i''' {
for (i = 5; i <NF; ++i) {
foo = foo ' ' $i;
}
printf('%-22s%sn', $2 ' [' $3 ']:', foo);
foo = ''
}' </usr/include/sys/errno.h
;;
*)
echo >&2 'errno: can't figure out whether '$i' is a name or a number.'
usage
;;
esac
done

这个程序通用化了吗?是的,非常理想。它同时支持数字和符号名称。另一方面,它不知道关于可能具有相同格式的其他文件的信息,比如 /usr/include/sys/signal.h。可以容易地扩展它来做到这点,但是对于这样一个便利的实用能够程序,简单地创建一个名为“signal”的拷贝来读取 signal.h,同时使用“SIG*”作为模式来匹配名称,这样会更容易。

虽然这仅比对系统头文件使用 grep 方便一小点,但是它更不容易出错。它不会因为考虑不周的参数而产生无用的结果。另一方面,如果没有从头文件中找到给定的名称或数字,它不会产生诊断信息。它也不会费心去纠正某些输入错误。而且,由于命令行实用程序从来没有打算在自动化的环境中使用,因此它的上述特性无可非议。

另一个例子可能是取消对输入排序的程序(请参阅 参考资料 以获得指向此实用程序的链接)。这相当简单;也就是读入输入文件,以某种方式存储它们,然后生成一个随机顺序来输出那些行。这是一个几乎具有无限应用前景的实用程序。编写这个实用程序也比编写排序程序容易得多;例如,您不需要指定您没有对哪些键排序,或者是您希望按字母顺序、词法顺序还是按数字顺序随机排序。棘手的部分在于读入可能非常长的行。事实上,上面提供的版本在搞欺骗;它假设所读入的行中没有空字节。纠正这个问题要困难多了,我在编写它时懒得去理会它。

结束语

如果您发现自己在重复执行某个任务,可以考虑编写一个程序来完成这个任务。如果事实证明该程序更通用化一点是合理的,那就通用化它,这样您就编写了一个实用程序。

不要在您第一次需要某个实用程序的时候设计它。要等到您具有一些经验之后才着手设计。请随意地编写一两个原型;优秀的实用程序比糟糕的实用程序更能证明所花的时间和研究工作的价值。如果原先设想的出色实用程序最终却在您编写它之后成为无用之物,不要感到遗憾。如果您发现自己对新程序的缺点感到沮丧,您只需再执行另外一个原型化阶段。如果结果证明它是无用的,不奇怪,有时会发生这样的事情。

您要寻求的是这样一个程序,它查找您的最初使用模式之外的通用应用。我编写 unsort 是因为,我希望找到一种从旧的 X11“rgb.txt”文件中获得随机颜色序列的容易途径。从那以后,我将它用于令人难以置信的大量任务中,这些任务都不是为了生成用于调试和基准排序例程的测试数据。

优秀的实用程序能够为您在所有不很理想的作品上所花的时间带来回报。要做的下一件事情是使它对其他人可用,以便他们能够试验它。也要使您失败的尝试对其他人可用,也许其他人对某个实用程序具有您所不需要的用途。更重要的是,您的失败的实用程序也许是其他某个人的原型,从而给每个人带来一个美妙的实用程序。

参考资料

Brian W. Kernighan 和 Rob Pike (Prentice Hall, Inc., 1984)所著的 The

UNIX Programming Environment 是所有程序员书架上的必备读物。您还可以在贝尔实验室的 CSR

页面上 下载本书的全部例子代码。

从 GNU Web 站点软件页面 下载核心、文件、shell 和文本实用程序。

您可以下载本文中描述的 “unsort”实用程序的源代码。

在 freshmeat 网站上可以找到关于 BSD 实用程序 jot 和 rs 的更多信息。

在“开发 Linux 命令行实用程序”(developerWorks,2002 年 6 月)一文中阅读关于代码编写入门的最佳实践和心得。

在 developerWorks 上 David Wheeler 的 Secure programmer 专栏 中学习如何编写安全的应用程序,验证和检查输入,防止缓冲区溢出,等等。

developerWorks 上的“Bash by example”系列将帮助您开始编写 shell 脚本。

教程“Building a cross-platform C library”展示了如何将现有 C 程序或模块(或实用程序)转换为共享库(developerWorks,2001 年 6 月)。

Peter Seebach 以前曾在 developerWorks 上撰写过 关于如何将 UNIX 实用程序看作组件体系结构的系列。

获得 developerWorks 订阅(即以前的 Toolbox 订阅),从而获得 IBM 推出的最新软件的 CD 或下载包,以便在 IBM 软件开发平台(IBM Software Development Platform)上生成、测试和展示应用程序。

欲获得 AIX 上的 Linux,请访问 用于 Linux 应用程序的 AIX 工具箱。

要在 Linux 上使用最新的 IBM 工具和产品来开发应用程序,请访问 Speed-start your Linux app 站点。

在 developerWorks

Linux 专区 可以找到为 Linux 开发人员准备的更多参考资料。

关于作者

Peter Seebach 编写实用程序已经有很长时间了。在他编写的实用程序中,他多次使用过其中的大约十分之一,这似乎是相当好的业绩。您可以通过电子邮件 [email protected] 联系他。


php爱好者站 http://www.phpfans.net 为phper提供一切资讯.
相关阅读 更多 +
排行榜 更多 +
辰域智控app

辰域智控app

系统工具 下载
网医联盟app

网医联盟app

运动健身 下载
汇丰汇选App

汇丰汇选App

金融理财 下载