捐赠?

Harley Hahn
个人主页

给 Harley
发送留言


来自 Harley Hahn 的
个人寄语

Unix 之书
主页

搜索

章节列表

目录

插图列表

各章...
   1   2   3
   4   5   6
   7   8   9
  10  11  12
  13  14  15
  16  17  18
  19  20  21
  22  23  24
  25  26

术语表

各附录...
  A  B  C
  D  E  F
  G  H

命令
摘要...

• 按字母顺序
• 按类别

Unix-Linux
年表

互联网
资源

错误与
勘误

推荐语


教师
与学生
资料...

主页
与概述

练习
与答案

Unix 模范
课程与
课程大纲

教师用
PowerPoint 文件

第 8 章...

立即可用的程序 (Programs to Use Right Away)

当你在 Shell 提示符后输入一条命令时, 你实际上是在告诉 Shell 运行那个名字 对应的程序。例如,当你输入 date 命令时,你就是在请 Shell 运行 date 程序(*)。

* 脚注

严格说来,这并不是事情的全部。 有些命令是 Shell "内置"的,也就是说 它们并不是真正独立的程序。

不过在此时此刻,区分独立程序和 内置程序还不重要。

Unix 有数以千计的不同程序,这就意味着 你可以输入的命令也有数千条。其中许多 程序要求你先懂一些理论。比如,在使用 文件系统命令之前,你需要先了解文件系统。 另一些程序则复杂到需要花大量时间才能 掌握。Unix 的两个主要文本编辑器 vi 和 Emacs 就是如此。它们是非常有用的 程序,但极其复杂。事实上,在本书中,我 为 vi 专门写了一整章。

然而,也有一些程序不需要什么专门知识, 用起来也不特别困难。这些就是你马上 就能用的程序,在本章中,我们就来认识 其中的一些。

在为数众多的 Unix 程序中,我挑选了几个 我认为特别有用、特别有趣或特别好玩的。 我有两个目标。第一,我想让你知道这些 程序,因为它们确实有用、确实有趣 或好玩。第二,在本书前面我们已经谈了 那么多一般原理,我想让你有机会习惯 Unix 的 CLI(命令行界面)。传统上,人们 学习 Unix 的方式之一就是一边学一边玩得 开心。你我一起来延续这个传统吧。

跳至页首

在你的系统上找到程序:
whichtypewhence
(Finding a Program on Your System: which, type, whence)

正如我们在第 2 章所讨论的,Unix 并不是 某个特定的操作系统。它是一个操作系统 家族:Linux、FreeBSD、Solaris、AIX,如此 等等。而且,就连 Linux 本身也不是一个 系统。"Linux"这个名字指的是任何使用 Linux 内核的 Unix 系统,而 Linux 发行版 实际上有几百种之多。

尽管各种 Unix 有很多共同之处,但它们 附带的程序并不完全相同。例如,Berkeley 系 Unix(FreeBSD、NetBSD 和 OpenBSD)里 有些程序,在 Linux 系统上就找不到。 而且,即使你比较两台运行同一个 Unix 版本的计算机,你也可能发现程序不同。 这是因为在安装 Unix 时,对于应该安装 哪些程序有很大的选择余地,而每个人 做的选择并不一样。

没错,基本的 Unix 就是基本的 Unix, 大多数系统都具备所有重要的程序。 但无论你用的是哪种 Unix,本章中(或 本书其他地方)介绍的程序,在你的系统上 都可能没有。

因此,有两个重要的问题我想现在就讨论: 你怎么知道某个特定的程序在你的系统上 有没有?如果没有,你有什么办法吗?

查看你能否使用某个程序,最简单的办法 就是在 Shell 提示符后输入它的名字。 (记住,Shell 的工作就是解释你的命令。) 如果你的系统上有这个程序,总会有所 反应。如果没有,Shell 会告诉你它找不到 那个程序。不用担心这样尝试会出问题。 你输入一条不存在的命令,不会造成任何 麻烦,你只会看到一条错误消息。

检查一个程序是否可用的更精确的方法, 是使用 which 命令。which 的作用 是让 Shell 回答这样一个问题:如果我输入 某条特定的命令,会运行哪个程序?如果 这个问题有答案,说明该程序已经安装在 你的系统上,你就可以使用这条命令。 如果没有答案,这条命令就不可用。

使用 which 命令时,在后面跟上一个或 多个程序的名字,例如:

which date
which date less vi emacs

第一条命令的输出如下:

/bin/date

这里,which 告诉你,如果你输入 date 命令,Shell 就会运行存放在 /bin/date 文件中的程序。如果你还 不了解 Unix 文件系统(我们将在第 23 章 讨论),这个名字暂时还对你没有意义。 不用着急。重要的是 which 找到了 可以运行的程序,这就告诉你 date 在你的系统上是一条有效的命令。

如果你问 which 一个并不存在的程序, 会发生什么?例如:

which harley

这取决于你所用的 which 版本,有两种 可能。第一种是什么也没发生,也就是没有 任何输出。这意味着 which 找不到你 指定的程序。这是许多 Unix 程序的一个 特点:如果它们没什么可说的,它们就不说。 (要是更多的人有同样的哲学就好了。)

第二种可能是你会看到一条错误消息。 下面是一条典型的消息:

/usr/bin/which: no harley in (/usr/local/bin:/usr/bin:
  /bin:/usr/X11R6/bin:/home/harley/bin)

which 在这里告诉你,它在你的搜索路径 中找不到名为 harley 的程序。等我们讲 各种 Shell 时会讨论搜索路径。眼下重要的 是要明白:因为 which 找不到名为 harley 的程序,你在自己的系统上就 不能使用 harley 命令。

那么,如果你想试试本章中的某个程序, 而它似乎不在你的系统上,你该怎么办? 尽管看起来理所当然,但请先检查你的 拼写。聪明人时时刻刻都会犯拼写错误, 你我也不例外(*)。

* 脚注

事实上,既然你我都比大多数人聪明, 我们大概是拼写错误犯得更多的那些人, 因为我们的头脑转得太快了。

如果你命令拼写正确却还是找不到,你有 几个选择。第一,把它放下不管。本章有 各种各样的程序可以试,其中大部分在你 的系统上都会有。

第二,如果你能使用其他 Unix 系统,可以 在那些系统上试试,看它们有没有这条 命令。

第三,如果你使用的是共享系统,你可以 请系统管理员把这个程序安装到你的系统上。 系统管理员都很忙,但对内行的人来说, 安装一个新程序花不了多少时间,所以 只要你有礼貌、会说话,就可能如愿以偿。

最后,如果你用的是自己的 Unix 计算机,你 可以自己安装这个程序。具体怎么装,因 Unix 的种类不同而不同,可惜这超出了本章 的范围。也许你能在本地找到一位发烧友, 他能就你的具体系统帮你一把。

如果你把 Bash 用作 Shell(见第 11 章), 那么除了 which 命令,还有一个替代品 type,例如:

type date

如果你使用 Korn shell(也在第 11 章), 你可以使用 whence 命令:

whence date

typewhence 命令有时会比 which 显示出更详细的信息。在某些 情况下这很有用。不过就实际用途而言, which 大多数时候都够用了。

在我们离开本节之前,我想问问你:如果你把 which 命令用在它自己身上,你认为会 发生什么?换句话说,which which is which? (到底哪个 which 才是 which?)试一下:

which which

如果你使用 Bash,试试:

type which
which type
type type

跳至页首

怎样停止一个程序?
(How Do You Stop a Program?)

大多数 Unix 命令完成一件事后就会自动 停止。例如,在下一节中我们会认识 date 命令。它做的只是显示时间和 日期,然后就停止,把你交还给 Shell 提示符。

另一些命令要复杂些。启动它们之后,你会 进入一个环境,在其中通过不断输入命令来 与程序本身交互。等你用完这个程序,就 输入一条特殊命令退出程序,这时程序停止, 你回到 Shell 提示符。

这类命令的一个例子是 bc,这是我们 本章稍后会讨论的计算器程序。使用 bc 时,你先启动它,然后想算多少次就输入 多少次。算完之后,你必须告诉 bc 你要退出。为此,你输入 quit 命令。

对于这类程序,总会有一条专门用来退出的 特殊命令。这条命令通常是 qquit。要确切地知道,你可以阅读 该程序的文档,Unix 联机手册中就有 (我们将在第 9 章讨论联机手册)。

除了特殊的退出命令,你往往还可以简单地 通过告诉程序"没有更多数据了"来停止它。 要做到这一点,按下 ^D (<Ctrl-D>),也就是 eof 键 (见第 7 章)。这常常是停止程序最轻松的 办法。例如,当你使用 bc 时,只要 输入 ^D,程序就停了。

如果其他办法都不管用,你也许可以按 intr 键来停止程序,这个键是 ^C 或 <Delete>(同样见第 7 章)。

— 提示 —

大多数自带交互环境的程序都附有某种 内置的帮助。要使用帮助功能,试试输入 helph? (问号)。

跳至页首

显示时间和日期:date
(Displaying the Time and Date: date)

date 命令是所有 Unix 命令中最有用的 命令之一。只要输入:

date

Unix 就会显示当前的时间和日期。下面是 一些输出示例。注意 Unix 使用 24 小时制。

Sun Dec 21 10:45:54 PST 2008

如果你住的地方使用夏令时,Unix 知道该 在什么时候把时钟拨快、什么时候拨慢。 这里的例子显示的是太平洋标准时间 (Pacific Standard Time)。到了夏天,就会是 太平洋夏令时间(Pacific Daylight Time)。

注意 date 同时给出时间和日期,所以 当你要知道现在几点时,就该用这条命令。 也有一条 time 命令,但它并不显示 时间,而是测量运行一个程序花了多长时间。

在内部,Unix 并不是真的按本地时间运行。 所有 Unix 系统都使用协调世界时 (Coordinated Universal Time,UTC),也就是 格林尼治标准时间(Greenwich Mean Time,GMT) 的现代名称。Unix 会在需要时悄悄地在 UTC 和你的本地时区之间换算。你所在本地时区 的有关细节是在安装 Unix 时指定的。

有时看看 UTC 时间是几点也很方便。要显示 UTC 时间,输入:

date -u

你会看到这样的时间和日期:

Sun Dec 21 18:45:54 UTC 2008

顺便说一句,这个时间与前面例子中的时间 是同一个时刻的 UTC 表示。

-- Harley 信息,请见附录 F,在那一附录里 我详细讲解了时区、24 小时制和 UTC。 我还示范如何在时区之间换算,并且 用几个实用的例子把这一切说明白。

跳至页首

显示日历:cal
(Displaying a Calendar: cal)

Unix 的一个好处是,它不是由委员会设计 出来的。当一位 Unix 程序员想要一个新工具 时,他可以自己写一个,然后加到系统里。 cal 命令就是一个很好的例子,它用来 显示日历。

要显示本月的日历,输入:

cal

要显示某一年的日历,只要指明那一年。 例如:

cal 1952

指定年份时,一定要把四位数都打出来。 例如,如果你输入 cal 52,得到的将是 公元 52 年的日历。想要 1952 年,就必须 用 cal 1952。你可以使用 1 到 9999 之间的任何年份。

— 提示 —

当你显示一整年的日历时,输出很长, 屏幕可能放不下。

如果日历的上半部分在你来得及看之前就 滚出了屏幕,你可以一屏一屏地显示输出, 办法是把输出送给 less 程序。例如:

cal 1952 | less

(我们将在第 21 章讨论 less。)

要显示某个特定月份的日历,除了年份,还要 把该月指明为 1 到 12 之间的数字 (1 = 一月)。例如,要显示 1952 年 12 月 的日历,输入:

cal 12 1952

你会看到:

    December 1952
Su Mo Tu We Th Fr Sa
    1  2  3  4  5  6
 7  8  9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31

如果你要某一个月份,就必须同时指明月和年。 例如,想要 2009 年 7 月的日历,你必须 输入:

cal 7 2009

如果你输入:

cal 7

你得到的将是公元 7 年的日历。

如果你不想看日期,而想看在一年中排第几天, 编号从 1 到 365(1 月 1 日 = 1;1 月 2 日 = 2; 依此类推),cal 程序也能如愿。只要在 date 这个名字后面加上 -j。 这里是一个例子。

cal -j 12 2009

输出为:

       December 2009
Sun Mon Tue Wed Thu Fri Sat
        335 336 337 338 339
340 341 342 343 344 345 346
347 348 349 350 351 352 353
354 355 356 357 358 359 360
361 362 363 364 365

假设你想弄清楚某一年是不是闰年。不懂 Unix 的人会去看这一年能不能被 4 整除, 如果能,就说它是闰年(*)。然而作为懂 Unix 的人,你还有另一种办法。你只要显示 那一年 12 月的"一年中第几天"的编号。 如果 12 月结束于第 366 天,你就找到了 一个闰年;否则就不是闰年。试试这几条 命令,自己看看:

cal -j 12 2000
cal -j 12 2008
cal -j 12 1776

顺便说一下,-j 中的 j 代表 Julian (儒略),也就是我们现代历法的名字,这种 历法每年 365 天或 366 天。更多信息请见 旁边的补充框。

* 脚注

唯一的例外是整百年份只有能被 400 整除 才是闰年。例如,2000 年是闰年,而 900 年不是。

儒略历与格里高利历 (The Julian and Gregorian Calendars)

平年 365 天、闰年 366 天这一做法源自 儒略历(Julian Calendar),它由凯撒 (Julius Caesar) 于公元前 46 年引入(*)。

但平均一年究竟有多长?如果四年中有三年 是 365 天、第四年是 366 天,那么一年 平均是 365.25 天。然而大自然并不总是 配合,这个数值偏长了 11 分 10 秒。到了 16 世纪,这点小误差累积成了大约 10 天, 也就是说,人们使用的历法已经和太阳、 星辰的运行对不上了。

为了解决这个问题,教皇格里高利十三世在 1582 年颁布法令,要求全世界修改历法, 使并非所有整百年份(1600、1700、1800 等等)都是闰年。只有能被 400 整除的整百 年份(如 1600 年或 2000 年)才是闰年。 这套方案称为格里高利历或新历(Gregorian or New Style Calendar)。为了把当时的历法 校准过来,格里高利进一步下令让 10 天 神秘地消失。

如果想特别精确,我们可以说现代(格里高利) 历法以 400 年为一个周期,一年平均为 365.2425 天。(一个有趣的事实是,按这套 历法,一个"纳世纪"大约是 π 秒。)

教皇在 1582 年颁布了他的法令。到 1587 年, 欧洲的天主教国家已经完成了这项改动。 但新教国家过了许多年才跟进,最后坚持不化 的是英国,直到 1752 年才做出改变。

英国一改,美洲殖民地也跟着改了。但此时 误差已经增大到 11 天。因此,如果你输入 这条命令:

cal 9 1752

你会看到,在 1752 年 9 月 2 日和 9 月 14 日 之间,有 11 天的空缺。这个空缺是让太阳和 星辰能够正常运转所必需的(至少在英国和 美国是这样)。

* 脚注

实际上,这套历法是一位研究生研制的, 但凯撒把名字签在了论文上。

跳至页首

Unix 的提醒服务:calendar
(The Unix Reminder Service: calendar)

我们刚才讨论的 cal 程序显示的是日历。 Unix 确实有一条名为 calendar 的命令, 但它完全不同。calendar 程序提供的是一种 提醒服务,依据的是你自己创建的一个记录 重要日子和消息的文件。

你要做的只是创建一个名为 calendar 的文件。calendar 程序会在当前目录中 寻找这个文件。(我们将在第 24 章讨论当前 目录。)在这个文件里,你写上若干行文本, 每行是一个日期,后面跟一个制表符,再后面 是一条提醒。例如:

October 21<Tab>Tammy's birthday
November 20<Tab>Alex's birthday
December 3<Tab>Linda's birthday
December 21<Tab>Harley's birthday

由于制表符是看不见的,你查看这个文件时 并不会看到它们:

October 21     Tammy's birthday
November 20    Alex's birthday
December 3     Linda's birthday
December 21    Harley's birthday (*)

* 脚注

接受礼物截止到一月底。

一旦你的 calendar 文件建好,你想什么 时候用都可以,随时输入 calendar 命令:

calendar

这样做时,程序会检查你的 calendar 文件,并显示所有日期是今天或明天的行。 如果"今天"是星期五,calendar 会显示 三天的提醒。

设计意图是:你时不时往自己的 calendar 文件里加几行,直到把生活 安排得井井有条。当然,在创建这样的文件 之前,你必须会用文本编辑器程序。最重要的 文本编辑器是 vi(第 22 章)和 Emacs。

如果你希望每次登录时自动运行 calendar 命令,就把它写进你的 .profile 文件(Bash 或 Korn Shell), 或者 .login 文件(C-Shell)。细节请看 各个 Shell 的章节。

有些 Unix 系统自带一个内置的 calendar 文件,里面有许多有意思的 条目。所以,即使你不想创建自己的文件, 也可以时不时运行一下 calendar 命令, 看看会发生什么。现在就试试吧。

关于 calendar 以及信息文件格式的更多 内容,请查看联机 Unix 手册中 calendar 的条目(见第 9 章)。要用的 命令是:

man calendar

跳至页首

关于你系统的信息:
uptimehostnameuname
(Information About Your System: uptime, hostname, uname)

有好几条命令可以用来显示有关你系统的 信息。首先是 uptime 命令,它显示你的 系统已经"在线"(也就是连续运行)了多长 时间:

uptime

下面是一些典型输出:

11:10AM up 103 days, 6:13, 3 users, load averages: 1.90, 1.49, 1.38

这里表示系统已经运行了 103 天 6 小时 13 分钟,当前有 3 个用户标识登录着。最后三个 数字分别表示过去 1 分钟、5 分钟和 15 分钟内 等待执行的程序数量的平均值。这些数字让你 对系统的负载有个概念。负载越高,系统做的 事就越多。

要查看你的计算机叫什么名字,使用 hostname 命令。如果你习惯登录多台 计算机,这会很有用。如果你忘了自己正在 用哪台系统,只要输入:

hostname

uname 命令向你显示操作系统的名字。 例如,你可能输入:

uname

然后看到:

Linux

要了解操作系统的更多细节,使用 -a(all,全部信息):

uname -a

下面是一些输出示例:

Linux nipper.harley.com 2.6.24-3.358 #1
Mon Nov 9 09:04:50 EDT 2008 i686 i686 i386 GNU/Linux

这里最重要的信息是:我们使用的是 Linux 内核,版本为 2.6.5-1.358。

跳至页首

关于你自己的信息:
whoamiquota
(Information About You: whoami, quota)

whoami 命令显示你登录时使用的用户 标识。如果你有许多个用户标识,又忘了当前 用的是哪一个,这条命令就很方便。类似地, 如果你遇到一台别人忘了退出的计算机, whoami 会告诉你当前的用户标识。 只要输入:

whoami

如果你忽然失忆、忘了自己的名字, whoami 命令也很有用。看看自己的 用户标识也许会给你一点线索。

如果你的系统上没有 whoami 命令, 试试把下面三个词分开输入:

who am i

最后一条能显示关于你自己的信息的命令是 quota。在共享系统上,系统管理员有时 会限制每个用户允许使用的磁盘存储空间。 要查看你的限额,输入:

quota

注意:Unix 用 KB(千字节)来度量磁盘空间; 1 KB = 1024 字节(字符)。

跳至页首

关于其他用户的信息:
userswhow
(Information About Other Users: users, who, w)

在早年间,Unix 计算机是共享的,大多数 时候一台系统上会有多个用户同时登录。 事实上,非常大的 Unix 计算机可以同时支持 几十—甚至几百—个用户。为了知道 当前都有谁登录着,你可以使用几条命令。 如果你与其他用户共享一台系统,这些命令 到今天仍然很有用。

最简单的一条是 users。只要输入:

users

你就会看到当前所有登录着的用户标识列表, 例如:

alex casey harley root tammy

你在第 4 章应该记得,在 Unix 系统内部, 只有用户标识才有真正的身份。真实的人 —也就是用户—由他们的用户标识 来代表。因此你不会看到真实人物的姓名, 只会看到用户标识。

如果你使用共享系统,users 命令很有用。 根据我的经验,意识到别人和你同时在用同一台 系统,是一种令人愉快的感觉,尤其是在深夜 独自一人在办公室或终端机房里工作时。仅仅 知道外面还有别人,就会让你感到彼此相连。 遗憾的是,许多人完全不知道这是什么感觉, 因为他们的经验就是每人都有自己的计算机。 他们不知道共享一个系统是什么滋味。

你可能会问:如果你是在自己的计算机上使用 Unix,用 users 命令还有什么意义吗? 多数时候答案是"没有":看到只有你一个人 登录着,并不怎么令人兴奋。然而,当你使用 多个终端窗口或虚拟控制台(见第 6 章)时, 你分别登录在每一个上面,如果你运行 users 命令,你的用户标识会出现多次。 同样,如果你用几个不同的用户标识登录虚拟 控制台,它们也都会显示出来。所以,起码在你 觉得孤单的时候,你可以假装系统上还有别人。

下一条命令是我们在第 4 章讨论过的 who。这条命令显示的信息比 users 多。对每个用户标识,who 会显示终端名、该用户标识登录的时间,以及 (在合适的情况下)该用户标识是从哪台远程 计算机连到你的系统上的。

下面是在一台供人们远程连接的系统上运行 who 命令得到的输出:

tammy   tty1    Nov 10 21:25
root    tty2    Nov 11 15:12
casey   pts/0   Nov 11 10:07 (luna)
harley  pts/1   Nov 11 10:52 (nipper.harley.com)
alex    pts/2   Nov 11 14:39 (thing.taylored-soft.com)

在这个例子中,用户标识 tammy 登录在 终端 tty1 上,而 root(超级用户) 登录在终端 tty2 上。这些是主计算机上 的虚拟控制台。正如我在第 7 章提到的,名字 tty 常被用作"terminal"(终端)的缩写。 在这个例子里,恰好有一位名叫 Tammy 的用户 是系统管理员,她当前登录了两次:一次用自己的 用户标识,一次以超级用户身份。

继续看,我们发现另一个用户标识 casey 是从名叫 luna 的计算机登录 进来的,那台机器在本地网络上。最后,还有 两个用户标识 harleyalex 是通过互联网远程登录的。

如果你想了解你的系统上各用户标识的更多信息, 可以使用 w 命令。这个名字是 "Who is doing what?"(谁在干什么?)的缩写。 下面是一些输出示例:

8:44pm up 9 days, 7:02, 3 users, load average: 0.11, 0.02, 0.00
USER   TTY     FROM LOGIN@ IDLE    CPU  PCPU   WHAT
tammy  console -    Wed9am 2days  2:38  0.00s  -bash
harley pts/1   -    12:21   0:00  0.01s 0.01s  w
alex   ttyp0   luna 13:11  20:18 43.26s 1.52s  vi birdlist

输出的第一部分显示系统统计信息,我们在本章 前面讨论 uptime 命令时见过。这里 表示系统已经运行了 9 天 7 小时 2 分钟, 当前有 3 个用户标识登录着。最后三个数字 分别表示过去 1 分钟、5 分钟和 15 分钟内 等待执行的程序数量的平均值。这些数字让你 对系统的负载有个概念。负载越高,系统做的 事就越多。

第一行之后,我们看到八列信息,如下所述。

USER: 当前登录在系统上的用户标识。 这里是 tammyharleyalex

TTY: 各用户标识所使用的终端名。

FROM: 该用户标识是从哪台远程计算机 登录进来的。在我们的例子中,tammyharley 是直接在主机上登录的,而 alex 是从另一台名为 luna 的 计算机登录进来的。

LOGIN@: 该用户标识登录的时间。

IDLE: 距离该用户最后一次按键过去了 多久。这称为 IDLE TIME(空闲时间)。在这个 例子中,tammy 已经空闲了大约 2 天, alex 空闲了 20 分 18 秒。如果你正在 等一个耗时的作业结束,而等待时并没有打字, w 命令就会显示你在空闲。

JCPU: 自登录以来所有进程使用的处理器 时间。其中"J"代表"jobs"(作业)。

PCPU: 当前进程使用的处理器时间。 其中"P"代表"process"(进程)。

处理器时间以秒(例如 20s)或分秒 (例如 2:16)表示。在计算机很昂贵、 处理器时间被视为宝贵资源的年代,这些数字 更有价值。

WHAT: 当前正在运行的命令。在我们的 例子中,tammy 运行的是 Bash Shell; alex 正在用 vi 编辑器(见 第 22 章)编辑一个名为 birdlist 的 文件;harley 正在运行 w 命令。

把上面这些信息合在一起,我们可以推断出: tammy 已经在控制台登录着,Shell 提示符在那里待着,大约 2 天什么都没做。 也许她就是系统管理员,喜欢随时保持登录。 我们还看到 alex 正在编辑文件,但已经 20 多分钟没有动静了,也许他或她去休息了 一会儿。

注意,每当你运行 w 命令时,你都会 看到自己正在运行 w 命令。在我们的 例子中,harley 就是这种情况。

默认情况下,w 命令显示系统上所有 登录着的用户标识的信息。如果你只想知道 某一个用户标识的情况,可以在命令后面加上 那个名字。例如,假设你刚刚登录,输入 users 命令看看还有谁登录着。你看到:

alex casey harley tammy weedly

如果你想看看 weedly 在干什么, 输入:

w weedly

名称的由来

CPU


在大中型机时代,计算机的"大脑"— 也就是我们现在所说的处理器—体积大得 需要一个很大的机箱,这个机箱称为中央处理机 (central processing unit)或 CPU。

如今,尽管大多数人不再使用大型机,我们 仍然把 CPU 当作"processor"(处理器)的 同义词来用。

跳至页首

临时锁定你的终端:lock
(Locking Your Terminal Temporarily: lock)

正如我在第 4 章提到的,登录着的时候离开 计算机是个坏主意。可能会有人走过来,用你的 用户标识输入命令,制造一大堆麻烦。例如, 一个顽皮的人可能删掉你所有的文件,以你的 名义给系统管理员发无礼的邮件,下载淫秽内容, 如此等等。

然而,如果你确实需要暂时离开终端一会儿, 还要退出登录再重新登录,就挺烦人了。特别是 当你用一个或多个终端窗口登录到远程主机, 或者你已经把整个工作环境布置成自己最喜欢的 样子时,更是如此。

你可以改用 lock 命令。这是告诉 Unix, 你想临时锁定自己的终端。终端会一直保持锁定, 直到你输入一个特殊的口令。要使用这条命令, 只要输入:

lock

Unix 会显示:

Key:

输入你打算用来解锁终端的口令。这个口令 可以是任何你想要的;它和你的登录口令毫无 关系。事实上,最好别用你的登录口令。你输入 时 Unix 不会把口令显示出来,以防别人正在看 你的屏幕。你输入口令后,Unix 会显示:

Again:

这是请你把口令再输一遍,以确保你没有输错。

一旦你输入并再次输入了这个特殊口令,Unix 就会冻结你的终端。无论别人在终端上敲什么, 都不会有任何反应,直到你输入口令为止。 (别忘了按 <Return> 键。)你一输入 口令,Unix 就会重新激活你的终端,你可以 继续工作。

— 提示 —

如果你登录了一台或多台远程主机,你应该 逐个会话分别锁定。

如果你在必须共用计算机、而且有人在排队等待 的地方工作,那么锁定终端就走、而且离开很长 时间(比如说去吃一顿晚饭)是不合礼仪的。 由于 Unix 诞生于一种以共享为基础的环境, lock 命令自带一个限制:终端在一段 指定的时间后会自动解锁。

默认情况下,lock 会把终端冻结 15 分钟。 不过,如果你想改变这个默认值,某些版本的 lock 允许你在输入命令时指定另一个 时限。在命令名后面留一个空格,然后输入 -(连字符),再跟一个数字。例如,要 把终端锁定 5 分钟,输入:

lock -5

你可能会问,如果有人锁定了终端然后一去不返 怎么办?最终命令会超时并解锁。如果在锁定 超时之前需要重新激活终端,系统管理员可以输入 root(超级用户)口令。lock 总是接受 root 口令,有点像万能钥匙。

但要记住,如果你锁了终端又不回来,总会有人 过来,发现你的终端被重新激活并且登录在你的 用户标识下。他们用你的用户标识造成的任何 麻烦,都要由你负责。

跳至页首

让 Unix 提醒你
什么时候该走:leave
(Asking Unix to Remind You When to Leave: leave)

你知道,在计算机上工作很容易让人入迷, 一不留神就忘了时间。为了帮你尽到世俗的 责任,只要输入这条命令:

leave

顾名思义,你可以用 leave 提醒你 该走了。你也可以用它提醒你该休息一下了。 例如,如果你喜欢每隔一会儿就站起来伸展 一下筋骨,你可以请它比如说在 20 分钟后 提醒你一次。

当你输入这条命令时,leave 会问你要 一个时间:

When do you have to leave?

hhmm 的形式(小时后面跟分钟)输入 你想离开的时间。例如,如果你想在 10:33 离开, 输入 1033

你可以用 12 小时制或 24 小时制输入时间。 比如 1344 表示下午 1:44。如果你输入 的小时数小于或等于 12,leave 就认为它 指接下来 12 小时之内的时间。例如,现在是晚上 8:00,你输入 855,leave 会把它 理解为晚上 8:55,而不是早上 8:55。

输入 leave 命令的另一种方式,是把时间 直接写在命令行上。在命令名后面留一个空格, 然后输入时间。例如,要在 10:30 离开, 输入:

leave 1030

如果你需要在若干时间间隔之后离开,输入一个 +(加号),后面跟分钟数。例如,如果你 需要在 15 分钟后离开,用:

leave +15

注意不要在 + 字符后面留空格。

— 提示 —

你退出登录时,Unix 会丢弃尚未到时的 leave 命令。因此,如果你用了 leave,但随后退出又登录,就得 重新运行这个程序。

输入 leave 命令之后,Unix 会周期性地 检查还剩多少时间。在你指定的时间还有五分钟 时,Unix 会显示:

You have to leave in 5 minutes.

还剩一分钟时,你会看到:

Just one more minute!

时间到了,Unix 显示:

Time to leave!

从那一刻起,Unix 会不停地唠叨提醒你,一分钟 一次,直到你退出登录:

You're going to be late!

最后,在这样提醒十次之后,你会看到:

You're going to be late! That was the last time I'll tell you. Bye.

也许这个程序应该改名叫 mother(妈妈)。

— 提示 —

你可以在登录时自动运行 leave 命令, 办法是把这条命令放进你的初始化文件 (Bash 或 Korn Shell 用 .profile, C-Shell 用 .login)。

这就是说,每次登录时,系统都会问你打算工作 多长时间(或到什么时候)。这样一来,你就不必 自己盯着时钟:Unix 会替你盯着。

如果你这样使用 leave,而这一次会话 不想让它运行,只要在第一个提示符后按 <Return> 键,程序就会中止。

— 提示 —

当你在较短时间内要做某件事、需要有人提醒时, leave 程序非常顺手。例如,假设你工作 时喜欢每 15 到 20 分钟就站起来伸展一下, 但你总是太投入而忘了休息。你只要输入这条 命令:

leave +20

15 分钟后(提前 5 分钟),leave 会用 一条警告引起你的注意。接下来的 5 分钟里, leave 还会再显示几条警告。

休息之后,再输入一次这条命令,你就会在又 15 分钟后得到提醒(*)。

* 脚注

杰出的医生 Janet G. Travell(1901-1997) 曾担任美国总统 John Kennedy 和 Lyndon Johnson 的私人医生,她是骨骼肌肉疼痛与功能障碍以及 慢性疼痛治疗方面的专家。

在撰写她的经典著作Myofascial Pain and Dysfunction: The Trigger Point Manual 期间,Travell 医生发现,为了保持身体的舒适 与健康,她需要每 15 到 20 分钟就短暂休息 一次。休息时,她会站起来,走动走动,拉伸 一下。

你会发现,工作期间这样休息一次会让你舒服 得多,特别是如果你受头痛、背痛或眼疲劳的 困扰。只要用 leave +20 这条命令来提醒 你何时该走动和拉伸。一旦你开始养成这个习惯, 几个小时之内就会感到舒适度有所改善。 (这又是"使用 Unix 有益身心健康"的一个例子。)

跳至页首

一个内置计算器:bc
(A Built-In Calculator: bc)

Unix 中最有用(也最不被重视)的程序之一就是 bc,一台功能完备、可编程的科学计算器。 许多人懒得去学 bc 怎么用。"我才看不上 bc 呢,"他们嗤之以鼻,"没人用它。" 别被误导了。一旦你学会怎么用 bc,就会 发现它做快速计算时价值不菲。

如果你使用桌面环境(第 5 章),你很可能找到 某种基于图形用户界面的计算器程序。这些程序 看起来很好看—它们真的会在屏幕上画出 一台计算器的样子—但对于日常琐碎的计算 或大量的计算,bc 要好得多。而且 bc 是基于文本的,也就是说你可以在任何 终端的命令行上使用它。

讲解 bc 时,我先给一个简短的技术概述。 如果你不完全看懂那些数学和计算机术语,不要 紧。在接下来几节中,我会讲解如何用 bc 做基本计算(这很容易),并附上几个例子。

技术概述:bc 是一个完全可编程的数学 解释器,提供扩展精度。每个数字都按所需的位数 存储,你可以指定小数点右边最多 100 位的标度 (scale)。数值可以在 2 到 16 的任意进制下 运算,进制之间的换算也很方便。

使用 bc 有两种方式:一是从键盘输入 计算式,它会被立即解释执行;二是运行存放在 文件中的程序。bc 的编程语法与 C 语言相似。你可以定义函数,可以使用递归。 它有数组、局部变量和全局变量。你可以编写 自己的函数并存放在文件里,然后让 bc 自动加载并解释它们。

bc 自带一个函数库,其中有以下函数: sin、cos、arctan、ln、指数函数和贝塞尔函数。 (知道贝塞尔函数是什么的人,请举手……)

更多信息,你可以用下面这条命令显示 bc 的联机手册说明:

man bc

(我们将在第 9 章讨论 Unix 联机手册。)

跳至页首

bc 做计算
(Using bc for Calculations)

大多数时候,你会用 bc 做日常计算, 这很简单。要启动这个程序,输入:

bc

如果你想使用内置的数学函数库(见下文), 请用 -l(library,函数库)选项启动 程序:

bc -l

一旦启动 bc,并没有专门的提示符; 你只管一条接一条地输入计算式。每次你按 <Return> 键,bc 都会计算你输入 的内容并显示结果。例如,如果你输入:

122152 + 70867 + 122190

bc 会显示:

315209

现在你可以输入下一个计算式。如果你想在 同一行输入多个计算式,用分号把它们分隔 开(*)。bc 会把每个结果显示在单独 一行上。例如,如果你输入:

10+10; 20+20

你会看到:

20
40

* 脚注

你在第 10 章将会看到,在 Shell 提示符后 工作时,你也可以在同一行输入多条 Unix 命令,只要用分号把它们分隔开。

用完 bc 之后,通过告诉它"没有更多 数据了"来停止程序。为此,按 ^D, 也就是 eof 键(见第 7 章)。 另外,你也可以输入 quit 命令。

图 8-1 给出了 bc 提供的基本运算。 加法、减法、乘法、除法和平方根都很直接, 结果和你预期的一样。取模计算的是除法之后的 余数。例如 53%103。 指数运算指的是一个数的多少次幂。例如 3^2 表示"3 的 2 次幂",结果是 9。指数必须是整数,但可以是负数。 如果你使用负指数,要用括号把它括起来, 例如 3^(-1)

Figure 8-1:   bc:基本运算

运算符 含义
+加法
-减法
*乘法
/除法
%取模
^指数
sqrt(x)平方根

bc 遵循代数的一般规则:乘法、除法 和取模优先于加法和减法;指数优先于一切。 要改变求值顺序,使用括号。所以 1+2*37,而 (1+2)*39

除了基本运算,bc 还在一个专门的库中 提供了许多有用的函数。这些函数列在图 8-2 中。

Figure 8-2:   bc:数学函数

函数 含义
s(x)x 的正弦;x 以弧度为单位
c(x)x 的余弦;x 以弧度为单位
a(x)x 的反正切;x 以弧度为单位
ln(x)x 的自然对数
j(n,x)xn 阶(整数阶)贝塞尔函数

如果你想使用这个库中的函数,需要用下面这条 命令启动 bc:

bc -l

使用这条命令时,bc 会自动把标度因子 设为 20(见下文)。

如我前面所说,bc 可以计算到任意精度。 也就是说,它会用尽可能多的位数来完成一次 计算。比如,你可以让它把两个 100 位的数相加。 (这个我试过。)

但是默认情况下,bc 会假设你在用整数 工作。也就是说,bc 不保留小数点右边 的任何数字。如果你想使用小数值,就需要设置 一个标度因子,告诉 bc 你要在小数点 右边保留几位。为此,把 scale 的值设成 你想要的标度因子。

例如,要求在小数点右边保留三位,输入:

scale=3

此后,所有运算都保留三位小数。多出来的 数字会被截掉。

如果你任何时候想查看标度因子是多少, 只要输入:

scale

bc 就会显示当前的值。

当你启动 bc 时,scale 的值 自动被设为 0。最常见的错误之一,就是 没有设置标度因子就开始计算。比如,假设你 刚刚启动 bc,然后输入:

150/60

bc 显示:

2

接着你输入:

35/60

bc 显示:

0

最后你总算明白问题出在哪里了。你的结果被 截断了,所以你需要设置一个合适的标度因子:

scale=3

现在 bc 就会显示你想看到的结果了。 (试一试。)

记住,当你使用数学函数库时,bc 会自动 以 20 作为标度因子开始。由于这个原因, 许多人启动 bc 时总是用 bc -l, 即使他们并不打算用数学函数库。(我一直这么干。)

跳至页首

bc 中使用变量
(Using Variables With bc)

bc 远不止是一台计算器。它实际上是一 门功能完备的数学编程语言。和所有编程语言一样, bc 允许你设置和使用变量。

变量是一个有名字、有值的量。在 bc 内部, 变量名由一个小写字母构成;也就是说,共有 26 个 变量,从 az。(一定不要用 大写字母;那些字母是在处理进制时用的— 见下文。)

要设置变量的值,使用 =(等号)字符。 例如,要把变量 x 的值设为 100, 输入:

x=100

要显示变量的值,只要输入它的名字。例如:

x

bc 会显示该变量的当前值。默认情况下, 除非你另行设置,所有变量的值都被认为是零。

你会发现,使用变量既简单明了,又能大大增强 你用 bc 工作的能力。下面这个例子 说明了基本原理。

Gaipajama 的土邦主( Maharaja )对你精通 Unix 印象深刻。为了表示对你的赏识,他提出 送你相当于你两倍体重的红宝石,每磅值 1000 美元,外加你三分之一体重的钻石,每磅值 2000 美元。(Gaipajama 的土邦主买宝石是 批发的。)

你体重 160 磅。土邦主的礼物值多少钱?要解 这道题,启动 bc,然后输入:

w=160
r=(w*2)*1000
d=(w/3)*2000
r+d

结果显示出来:

426000

也就是说,你的礼物价值 426000 美元。

且慢:土邦主一旦意识到自己的许诺要花他这么多 钱,便说:"我是说按你的磅数给你宝石吗?我 应该说的是公斤数才对。"

既然 1 公斤等于 2.2 磅,你连忙把 w 变量的值换算成公斤:

w=w/2.2

现在你重新输入红宝石和钻石价值的计算式:

r=(w*2)*1000
d=(w/3)*2000
r+d

新的结果显示出来:

192000

就这样,靠改用公制,这位土邦主(*)省下了 234000 美元。同时,他也让你演示了如何根据 变量的旧值为它设置新值,这里是 w=w/2.2

* 脚注

关于 Gaipajama 的土邦主的更多信息,请见 Hergé 所著的Cigars of the Pharaoh

跳至页首

bc 使用不同的进制
(Using bc with Different Bases)

正如你所料,bc 通常使用十进制运算。 (如果你不知道"进制"是什么,这一节你可以 放心跳过。)然而,有时你会想用另一种进制来 计算。例如在计算机科学中,有时需要使用十六进制 (hexadecimal)、八进制(octal)或二进制 (binary)。

bc 允许你为输入和输出分别指定不同的 进制。为此,有两个特殊的变量可以设置: ibase 是输入所用的进制,obase 是输出所用的进制。

例如,如果你要以十六进制显示结果,输入:

obase=16

如果你要以八进制输入数字,用:

ibase=8

在上一节中我说到,默认情况下变量的值是零, 直到你设置它。ibaseobase 是例外:它们被自动初始化为 10,以便你 以十进制工作。如果你想用另一种进制,可以把 这两个变量中的任意一个设为 216 之间的任何值。

你应当明白,ibaseobase 的值 并不影响 bc 内部如何运算数字。它们唯一 的作用,是指定数字在输入或输出时应如何换算。

要在大于 10 的进制下工作,bc 把 10、11、 12、13、14、15 这些值分别用大写字母 ABCDEF 表示。一定要记得使用大写;如果你用 小写,bc 会以为你在引用变量,结果就 不是你想要的了。

为了方便,无论你设置了什么输入进制,都可以 使用这些大写字母。例如,即使你在十进制下 工作,表达式 A+1 的值也是 11

和所有变量一样,你可以单独输入 ibaseobase 这两个名字来 查看它们的当前值:

ibase; obase

不过你必须小心。一旦你设置了 obase, 所有输出都会以那个进制显示,你可能读不懂 看到的内容。例如,如果你输入:

obase=16
obase

你会看到:

10

这是因为所有输出都要以十六进制显示,而在 十六进制中,"16"这个值写作 10

同样,一旦你改了 ibase,你输入什么 就要当心。例如,假设你设置了:

ibase=16

现在你想把 obase 设为十进制,于是 输入:

obase=10

然而你忘了,输入现在按十六进制解释,而十六 进制的 10 实际上是"16"。于是,你刚刚 把 obase 设成了十六进制。

要避免这类错误,请使用字母 AF,无论 ibase 取什么值,它们都 保持同样的数值。因此,如果事情变得混乱,你 总可以通过输入下面这条命令来重置进制:

obase=A; ibase=A

下面有两个换算进制的例子。第一个,你想把两个 十六进制数 F03E3BAC 相加。 输入:

obase=16
ibase=16
F03E + 3BAC

bc 显示结果:

12BEA

第二个例子,你想把十六进制数 FFC1 换算成 二进制(基数 2)。先重置进制:

obase=A; ibase=A

然后输入:

obase=2; ibase=16
FFC1

bc 显示结果:

1111111111000001

— 提示 —

bc 远不止是一台计算器程序。它是一套 精密的数学编程系统,有自己的内置编程语言。 本章中我只讲解了最基本的功能。不过,如果你 有时间,我建议你去探索一下 bc,了解 它还能为你做些什么。

最好的办法是阅读 bc 的联机手册页。 要这样做,命令是:

man bc

(我们将在第 9 章讨论 Unix 联机手册。)

跳至页首

逆波兰表示法
(Reverse Polish Notation)

最初,bc 程序是基于一个名为 dc (desk calculator,桌面计算器)的程序编写的。 dc 是最古老的 Unix 程序之一,甚至比 C 编程语言还早。事实上,dc 的最初版本是 1970 年用 B 语言写成的,B 是 C 的祖先。我们 一会儿还会谈 bcdc 的关系。 不过眼下,我想先给你讲讲 dc— 它本身就是一个非常有趣的工具—因为和 bc 一样,它也是你马上就能用的程序。

先来个技术性的描述:dc 是一个交互式、 任意精度的计算器,它用逆波兰表示法来模拟一台 栈机。

显然,dc 不是那种人见人爱的程序:如果 你对数学或计算机科学没有兴趣,尽管跳过这段 讨论。不过,如果你偏向技术,dc 有几个 理由值得你去理解。

第一,如我所说,dc 使用所谓逆波兰 表示法。虽然这个概念对现在的你可能毫无意义, 但如果你学习数学、工程或计算机科学,这是个 你应该了解的重要概念。

第二,要了解 dc,你需要理解栈(stack) 的概念(我会解释),这对计算机科学家和程序员 来说是个重要概念。

最后,使用 dc 所需要的思维方式,正是 使用 Unix 所需要的思维方式。因此,花一点时间 了解 dc,并且—如果你有兴趣的 话—自学如何使用它,会让你离成为一个 Unix 中人更近一步。

我们对 dc 的讨论将从解释逆波兰表示法 开始。下一节我们转向栈的概念。一旦你理解了 这两个基本思想,你就能借助联机文档自学如何 使用 dc

1920 年,一位名叫 Jan Lukasiewicz (1878-1956)的波兰数学家注意到,如果把运算符 放在操作数之前,我们书写算术表达式的方式可以 变得更紧凑。这样做之后,我们就能不用括号和 方括号来写出复杂的表达式。看一个小例子就能 明白这个思想。

假设你想把 34 加 25,然后把和乘以 15。按 标准记法,你会写成:

(34 + 25) * 15

因为运算符—这里是 +(加号)和 *(乘号)—放在操作数之间,我们 把这称为中缀表示法(INFIX NOTATION)。

Lukasiewicz 的体系使用前缀表示法(PREFIX NOTATION),即我们先写运算符,后写操作数。 例如:

* + 34 25 15

要求前缀表示法的值,我们从左到右逐个处理各 元素。在这个例子中,我们从 * 运算符 开始,它告诉我们要一得到两个数就做乘法。 接着我们遇到 + 运算符,它告诉我们 一得到两个数就做加法。

接下来,我们看到连着两个数 3425,于是执行加法,得到和 59。 记住这个 59,我们继续往下,遇到数 15。现在可以执行乘法 59*15, 得到最终答案 885

为了纪念 Lukasiewicz—他是著名的数学家、 逻辑学家和哲学家—前缀表示法常被称为 波兰表示法(POLISH NOTATION(*))。对计算机 科学家来说,波兰表示法之所以重要,是因为它 紧凑、直接,而且能高效求值。

* 脚注

Jan Lukasiewicz 于 1878 年 12 月 21 日出生在 加利西亚地区 Lwow 城,加利西亚是奥地利最大、 位置最靠北的省份。加利西亚是 1772 年第一次 瓜分波兰的产物。虽然从技术上说 Lukasiewicz 是奥地利人,但他血统上是波兰人,生来如此, 而 Lwow 这座城也是波兰人占多数。如今,Lwow 以 Lviv 之名为人所知,是乌克兰西部最大的城市。

说件有趣的事:我祖父 Irving Hahn (1895-1986)年轻时住在 Lwow,在那里当理发师 学徒。而且,我的生日也是 12 月 21 日。

1957 年,澳大利亚计算机科学家 Charles Hamblin 发表了两篇论文,提出在一种基于栈的计算系统中 使用波兰表示法的一个变体。(栈我们将在下一节 讨论。)他描述的这个变体是把运算符放在操作数 之后,也就是所谓的后缀表示法(POSTFIX NOTATION)。

为了说明后缀表示法,我们再来看上面那个表达式。 用后缀表示法,它看起来是这样:

34 25 + 15 *

求值时,我们从左到右处理各元素。首先看到两个数 3425,必须记住。接着看到 + 运算符,它告诉我们要把最后得到的两个 数相加。这里我们把 3425 相加 得到 59,必须记住。

接着我们看到数 15,也记下来。最后看到 * 运算符,它告诉我们要做两个数的乘法。 这里是 5915,相乘得到最终 答案 885

后缀表示法特别适合自动计算,因为表达式可以按 直接了当的方式从左到右求值:记住数字,遇到 运算符就施加运算。用中缀表示法时,括号和其他 各类优先级规则—比如乘法必须先于加法 做—往往要求把某些运算推迟到其他运算完成 之后。后缀表示法则不必如此。

为了纪念 Lukasiewicz,后缀表示法常被称为逆波兰 表示法(REVERSE POLISH NOTATION),简称 RPN。 多年来,波兰表示法和逆波兰表示法都在各种计算机 系统中得到应用。例如,Lisp 编程语言和 Tcl 脚本 语言使用波兰(前缀)表示法;Forth 编程语言和 PostScript 页面描述语言使用逆波兰(后缀)表示法。

RPN 最有名的用途,也许是作为 HP 计算器的 基础—科学家和工程师们已经用了许多年。 第一台这样的计算器是 1968 年推出的 HP 9100。

从那以后,RPN 计算器变得非常流行,因为一旦你 懂了 RPN,它就比传统的中缀表示法更快、更好用。 例如,使用 RPN 计算器时,每次计算的结果都会 立即显示。也就是说,你输入计算式的过程中就能 看到中间结果,这使得发现错误容易得多。使用传统 中缀表示法的计算器则不然:如果你输入一个按标准 优先级规则的表达式,在整个计算完成之前是显示 不出结果的。

1970 年,贝尔实验室的研究人员 Robert Morris 受到 HP 计算器的启发,用 RPN 开发了一个基于 Unix 的交互式计算器程序,取名 dc (desk calculator,桌面计算器)。dc 是一件很棒的工具,但它确实要求用户先学会使用 RPN。

几年之后,Morris 和另一位研究人员 Lorinda Cherry 又写了一个名为 bc 的程序,让 用户可以用更传统的中缀表示法来写计算式。 bc 的工作方式是把它的输入转换成 RPN, 然后调用 dc 来做实际的运算。换句话说, bcdc 的一个"前端"。这样 人们就可以用自己喜欢的体系:dc 的后缀 表示法,或者 bc 的中缀表示法。

多年以后,作为 GNU 项目(见第 2 章)的一部分, bc 被彻底重写为一个独立的程序。由于许 多现代 Unix(包括 Linux 和 FreeBSD)使用 GNU 工具集,很可能你今天用 bc 时,用的是一 个独立程序,它并不 依赖 dc 的程序。当然,dc 本身 仍然可以单独使用。

跳至页首

基于栈的计算器:dc
(The Stack-Based Calculator: dc)

看下面这个 RPN(后缀)表示法的例子:

34 25 + 15 *

dc 按我上一节描述的方式求这个表达式的 值:一次一个元素,从左到右读。dc 每遇到 一个数字,就必须记住这个数的值;dc 每 遇到一个运算符,就必须执行相应的运算,并记住结果。

问题就来了:dc 是怎么跟踪这些必须记住的 量的?答案是:用我们所说的栈(stack)。

在计算机科学中,有多种不同的数据结构(DATA STRUCTURE)用来存放数据。每种数据结构都按 自己一套严格的规则来存储和取出数据。最常见的 数据结构有表、链表、关联数组、哈希表、栈、 队列、双端队列(deque,两端都可进出的队列), 以及各种基于树的结构。本节我们集中讲栈,因为 dc 用的就是栈。

栈(STACK)是这样一种数据结构:其中的数据元素 按"后进先出"(last in, first out),也就是 LIFO 的过程,一次一个地存放和取出。它是这样 工作的。

栈一开始是空的。要存放一个数据元素,你把它 压入(PUSH)栈中。数据此时就位于栈顶(TOP)。 你可以按自己的意愿把任意多个数据元素一次一个 地压入栈。每压入一次,栈上所有元素都被往下压 一层。因此任何时候,栈顶装的都是最近压入栈的 数据。你只能从栈顶取出来获得栈中的数据。这样 做时,我们说你弹出(POP)了栈。

换句话说,当你弹出栈时,你取出的是最后压入栈 的那个值。这就是为什么栈被描述为 LIFO(后进 先出)。

要想象栈到底是什么样子,想想自助餐厅里那种 带弹簧的盘子立柱。盘子一次一个地被压入这个 "栈"。当你想要一个盘子时,你必须从栈顶弹出 最上面那个。你碰不到其他任何盘子。如果由于 某种原因你想要最底下那个盘子,你就得把上面 所有的盘子一次一个地全部弹出。

dc 程序正是以这种方式,用栈来解释以 RPN 书写的算术表达式。为此,dc 遵循 一个简单的过程:从左到右读表达式,一次一个 元素。遇到数值,就把它压入栈。遇到运算符,就 从栈中弹出适当数量的元素,执行运算,再把答案 压入栈。

为了说明这一点,我们来看前面那个例子:

34 25 + 15 *

下面是 dc 解释这个表达式的逐步骤说明:

1. 读到数值 34,把它压入栈。

   栈的内容:34

2. 读到数值 25,把它压入栈。

   栈的内容:25 34

3. 读到 +(加号)。要做加法需要两个 值,所以……

4. 从栈中弹出 2534 并相加。 把结果(59)压入栈。

   栈的内容:59

5. 读到数值 15,把它压入栈。

   栈的内容:15 59

6. 读到 *(乘号)。要做乘法需要两个 值,所以……

7. 从栈中弹出 1559 并相乘。 把结果(885)压入栈。

   栈的内容:885

如果你不能马上领会 RPN 的窍门,不用担心。你 随时可以通过练习 dc 来弄明白。事实上, 真正理解 RPN 最好的办法就是动手试验。

在我们的例子里,我在每一步都向你展示了栈的 内容。用 dc 时,你看不见栈。不过任何 时候你都可以用 p(print,打印)命令(*) 显示栈顶。为了说明它是怎么工作的,启动 dc,然后输入下面两行。(别忘了每行末尾 都要按 <Return> 键。)

34 25 + 15 *
p

* 脚注

正如我在第 7 章解释的,由于老式 Unix 终端把 输出打印在纸上,"print"(打印)一词常被当作 "display"(显示)的同义词使用。因此,dc 的打印命令显示的是栈顶的值。许多 Unix 程序都 遵循这个惯例。

你输入第一行后,dc 执行了计算,但你 什么也看不到。等你输入第二行(p 命令) 之后,dc 显示栈顶元素的值,这里是 885,也就是前一次计算的结果。

如果你想查看整个栈,使用 f 命令:

f

为什么 dc 不在你每次输入新行时自动 打印栈顶的值呢?答案是:如果 dc 在你 每输入一行时都打印点什么,你的屏幕就会被塞满。 相反,dc(和大多数 Unix 程序一样)尽量 保持沉默。要不要查看栈顶,由你根据需要决定。

为了帮你入门 dc,图 8-3 汇总了最重要的 一些 dc 命令。除了这份汇总,自学 dc 用法的最好办法是阅读联机手册(见 第 9 章)并动手实验。查看 dc 手册页的 命令是:

man dc

Figure 8-3:   dc:最重要的命令

命令 含义
q退出
p打印栈顶
n弹出栈并打印该值
f打印栈的全部内容
c清空栈(把栈变空)
d复制栈顶的值
r颠倒(交换)栈顶的两个值
+弹出两个值,相加,压入和
-弹出两个值,相减,压入差
*弹出两个值,相乘,压入积
/弹出两个值,相除,压入商
%弹出两个值,相除,压入余数
~弹出两个值,相除,压入商,再压入余数
^弹出两个值,以第二个为底、第一个为指数求幂,压入结果
v弹出一个值,取平方根,压入结果
k弹出一个值,用它设置精度

dc 在小数点之后保留的位数称为"精度" (precision)。默认值是 0 位。要改变 它,把你想要的位数压入栈,然后输入 k 命令。这个值想设多大都可以。例如,要把精度 改为 14 位,用:

14 k

要显示当前的精度,使用 K(大写 "K")命令。它会把当前的精度压入栈。然后你 可以用 p 命令显示实际的数值:

K p

最后,要停止 dc,你可以按 ^D 表示没有更多数据,也可以使用 q (quit,退出)命令。

— 提示 —

如果你喜欢数学或科学式的思考,你会发现玩 dc 很有意思。玩着玩着你会发现, dc 不只是个消遣。要学会使用这个程序, 你需要掌握逆波兰表示法以及如何使用栈这两个 都不算容易的概念。然而一旦你掌握了这些思想, 就会发现 dc 是一件高效、设计良好、 用起来顺手的工具。

如果你回头看第 1 章的最后,你会记得我就 Unix 说过两句一般性的话:Unix 用起来容易, 学起来难。先学基础,然后想学什么就学什么, 想按什么顺序就按什么顺序。

你能看出 dc 正符合这个完全相同的模式 吗?它用起来容易,学起来难。而且起步时你 先学基础(通过读本章),然后随自己的意愿 独自实验。

所以,如果你肯花时间练习 dc,你不单 是在学一个有趣的工具怎么用,你还是在训练 自己的头脑,让它以 Unix 中人应有的方式思考。

跳至页首



练习
(Exercises)

复习问题 #1:

说出三种用来停止程序的方法。

复习问题 #2:

你要显示时间用哪个程序?显示日期呢?

复习问题 #3:

calcalendar 这两个程序 有什么区别?

复习问题 #4:

怎样显示你所用计算机的名字?你的操作系统? 你的用户标识?

应用你的知识 #1:

Mickey Mouse 生于 1928 年 11 月 18 日。 显示那个月的日历。

Mickey 出生那天是星期几?

那是该年的第几天(1 月 1 日 = 1, 12 月 31 日 = 366)?

应用你的知识 #2:

元素镥(lutetium)是一种非常重、非常稀有、 银白色的金属。它被认为是世界上最贵的金属。 Gaipajama 的土邦主想请你帮他照看儿子。 报酬是每 2 小时 1 克镥,他要你干 5 小时。

假设黄金价格是每克 25.42 美元(美元计价), 而 1 克镥值 6 克黄金。用 bc 程序 算一算你的照看费值多少美元。答案要精确到 小数点后 2 位。

(做完之后,到互联网上查一查 Gaipajama 的 土邦主。)

进一步思考 #1:

在本章开头,我说过一句话:"传统上,人们 学习 Unix 的方式之一就是一边学一边玩得 开心。"对 Microsoft Windows 等其他操作 系统来说,情况通常并非如此。为什么?

进一步思考 #2:

userswhow 这三个 程序都显示当前登录在系统上的用户标识的信息。 我们真的需要三个不同的程序吗?

跳至页首