UNIX命令行惯用语ibm官方完整版本

11-30

当您到使用不同语言的国家去旅行时,可能需要掌握一些关键的日常用语,如“这个东西多少钱?”、“这是什么肉?”以及“洗手间在何处?”。记住这些简短的日常用语可以确保别人不会对您定购的三明治要价太高,并且在需要上厕所的时候,您知道该去何处。

UNIX 也具有自己的方言,在过去的 6 个月中,这个对话 UNIX 系列为 UNIX 命令行惯用语提供了速成教程。这个月我们将介绍一些有用的短语,它们能够使您立即成为地道的 UNIX 用户。带上牙刷,穿上舒适的鞋子,并且更新您的惯用语。我们要出门迎接阳光、沙滩和贝壳。(置身于阳光和沙滩中,面朝海滩,打开便携式计算机,然后阅读本专栏。可不要忘了抹点防晒油。)

开始学习之旅

在以前的对话 UNIX 专栏(请参见参考资料部分)中曾多次介绍了 find 命令,这是一种非常有用的实用工具,可用于扫描并处理各种文件,甚至整个 UNIX 文件系统。例如,我经常将 find 与 grep 或者 Perl 一起使用,以便对大量的文件进行处理。您需要了解在一大段代码中的何处定义了变量或常量吗?可以尝试下面的命令:

复制代码

代码如下:

$ find /path/to/src -type f | xargs grep -H -I -i -n string

该命令的输出是一个文件名列表,其中包含 string,包括行编号和匹配的特定文本。在每个匹配的文件名和行编号的前面分别加上了 -H 和 -n 选项。-i 选项忽略大小写。-I(大写“I”)跳过二进制文件。

您以前可能没有见过 xargs,它将使用列出的所有选项运行您所指定的命令,在本示例中是 grep,每次使用通过标准输入提供的一个参数。假设 /path/to/src 目录包含文件 a、b 和 c,使用 find 与 xargs 等价于:

复制代码

代码如下:

grep -H -I -i -n string a
grep -H -I -i -n string b
grep -H -I -i -n string c

事实上,搜索文件集是一项常见的工作,所以 grep 具有相应的选项以递归遍历整个文件系统层次结构。可以使用 -d recurse 或其同义词 -R 或者 -r。例如,可以使用:

复制代码

代码如下:

$ grep -H -I -i -n -R string/path/to/src

这个命令与 find/xargs 完成相同的任务。(您将发现,许多与文件相关的 UNIX 实用工具都具有递归选项。ls -R 可以递归地列出层次结构中的内容。chmod、chgrp 和 chown 使用 -R 可以递归地将模式、组和所有权变更应用到整个文件系统层次结构。在使用 chmod -R 时,请多加小心。如果删除了目录的执行位,比如 chmod -R a-x,您可能会使得一个目录变得不可使用。为了更具选择性,可以使用 find . -type f | xargs chmod a-x。)

那么,什么时候应该使用 find/xargs,什么时候应该使用 grep 呢?当需要具有一定的选择性时,可以使用 find。find 命令具有许多选项,使得您可以选择满足特定要求的文件,如“所有在午夜后修改过的、并由 Joe 拥有的常规文件”。否则,使用 grep -R 就可以了。

另一种实用工具可能比 find 使用起来更加方便,并且速度更快。如果您打算根据名称来查找一个文件,那么可以尝试使用 locate 来代替 find -name。locate 命令周期性地(大约每天一次,由系统管理员设置)为系统中所有的文件编制目录,并构建一个由路径和文件名组成的数据库。当您运行 locate 时,它将扫描其私有的数据库,尝试进行匹配。

例如,运行查询 locate '*.1',将得到名称以 .1 结尾的所有文件和目录。(前面的星号表示匹配任何字符串。)为了方便起见,运行 locate fish 命令与运行 locate '*fish*' 是相同的。

货币替换

有许多 UNIX 实用工具可以对文件进行修改。在大多数情况下,可以将经过修改的内容发送到标准输出,您可以使用重定向操作符对其进行进一步的处理(使用管道“|”)或捕获其中的结果(使用 > 或 >> 操作符)。

其他的实用工具(那些通常可以一次处理许多文件的工具)可以出于安全考虑而保留原始文件,并为修改后的内容生成一个新的文件。例如,您可以直接在命令行中使用 Perl 对文件进行处理。以下命令:

复制代码

代码如下:

$ perl -i.bak -pe 's//bdollar(s?)/buck/1/g' file.txt

将“dollar”替换为“buck”,将“dollars”替换为“bucks”。perl -i 命令在原地对 file.txt 进行修改,而 perl -i.bak 则为原始文件建立一个副本,并在其名称后面添加 .bak,以区别于新的、经过修改的版本。因此,如下的命令:

复制代码

代码如下:

perl -i.bak -pe 's//bdollar(s?)/buck/1/g' *

将为当前目录中每个文件创建一个备份。假设有文件 file1.txt、file2.txt 和 file3.txt,那么您将得到 file1.txt.bak、file2.txt.bak 和 file3.txt.bak。错误操作时常发生,所以建立备份是明智之举。

如果出现了错误,并且必须恢复原始文件,您只需输入:

复制代码

代码如下:

mv file1.txt.bak file1.txt

。但是,如果有数百个文件 需要进行重命名,那又应该怎么办呢?当然,您并不希望输入数百个单独的 mv 命令。相反,您可以输入下面的命令:

复制代码

代码如下:

foreach file in (*.txt)
do
mv $file.bak $file
done

它适用于一些简单的情况,如本示例中的情况。然而,这类任务非常常见,可以使用另一种特殊的实用工具,它能够更快速地完成这项任务。以下命令:

复制代码

代码如下:

$ rename 's//.bak$//' *.bak

执行了相同的任务。正则表达式 s//.bak$// 将命令行中列出的每个文件名后面的 .bak 删去,在本示例中是 * 或所有文件,并使用缩短后的名称作为目标文件名。

当文件名没有什么规律时,rename 命令尤其有用。例如,可以考虑下面这个目录中的内容,它看起来像一个大学一年级新生的信件集合。

复制代码

代码如下:

$ ls
RenT.txt bEErMoNey.txt gASmoNey.TXt

上面的 foreach 脚本无法处理这个问题,因为这些文件名毫无规律可循。而 rename 可以轻松地对其进行处理:

复制代码

代码如下:

$ rename 'y/A-Z/a-z/' *

正则表达式 y/A-Z/a-z/ 中的 y 操作符用于进行转换。转换工作需要两个列表:一个原始字符列表和一个替换字符列表。如果这两个列表大小相同,那么在这段文本中,将原始列表中第一个字符的实例替换为替换列表中的第一个字符。换句话说,在本示例中,每个大写“A”的实例都将替换为小写“a”、“B”替换为“b”,依此类推。文本中的小写字母保持不变。

如果您需要先对 rename 所执行的工作进行预览,那么可以添加 -n 选项。这个选项可以显示该命令所执行的工作,但并不真正地进行这些更改:

复制代码

代码如下:

$ rename -n 'y/A-Z/a-z/' *
RenT.txt renamed as rent.txt
bEErMoNey.txt renamed as beermoney.txt
gASmoNey.TXt renamed as gasmoney.txt
$ rename 'y/A-Z/a-z/' *
$ ls
beermoney.txt gasmoney.txt rent.txt

其中有一个缺点需要避免:在 UNIX 系统中,文件名是区分大小写的。一个目录中可能包含 Aa.Txt 和 aA.txT。如上所述,可以编写一条重命名规则将区分大小写的文件名转换为小写文件名,这样可能会与以前已存在的唯一的文件名发生冲突。在这种情况下,rename 将如何操作呢?让我们来看一下:

复制代码

代码如下:

$ rename -n 'y/A-Z/a-z/' *
Aa.Txt renamed as aa.txt
aA.txT renamed as aa.txt
$ rename 'y/A-Z/a-z/' *
aA.txT not renamed: aa.txt already exists
$ ls
aA.txT aa.txt

如果您希望在进行重命名的过程中删除现有的文件,那么可以添加 -f 标志。在这个示例中,将得到一个名为 aa.txt 的文件。那么哪个文件是其原始文件呢?因为 rename 按照字母顺序进行处理,后面的 aA.txT 文件是现在的 aa.txt。为什么要使用 -f 呢?如果两个文件是相同的,仅仅是名称不同,rename -f 会删除重复的文件。

不要删除重复的文件

文件管理是使用 UNIX 系统时非常重要的工作。系统中包含大量的配置文件。您可能拥有非常多的数据文件和个人文件。您可能不时地需要删除或覆盖某个有价值的文件。Shell 和一些文件管理实用工具可以帮助您避免灾难。

在 Shell 提示符处输入下面的命令。这些命令可以在 bash 中执行,但 zsh 和其他 Shell 也具有类似的选项。

复制代码

代码如下:

$ alias mv=mv -i
$ alias rm=rm -i
$ set -o noclobber

前两个命令分别在命令行中将 mv 替换为 mv -i、将 rm 替换为 rm -i。交互式的模式强制您对操作进行确认。

第三个命令在 Shell 中提供了一定的安全性。启用了 noclobber 之后,您就不会一不小心使用 > 重定向操作符覆盖某个文件:

复制代码

代码如下:

$ ls
secret.txt
$ cat > secret.txt
bash: secret.txt: cannot overwrite existing file

要禁用 noclobber,可以输入:

复制代码

代码如下:

set +o noclobber

。您还可以在任何时候使用 >|(一个小于号加上一个竖线)重定向操作符强制进行覆盖。

复制代码

代码如下:

$ cat secret.txt
I love green eggs and ham.
$ echo "No more secrets" >| secret.txt
$ cat secret.txt
No more secrets

关于本地的一些秘密

如果您真的希望发现一个城市,那么您得到当地的公众聚谈之处走访一下。下面是一些命令行的组合,相当于提供旅游资讯的 Zagat。

mkdir -p 可以快速地创建层次结构。使用 -p 选项后,mkdir 将为指定路径创建所有的目录和子目录:

复制代码

代码如下:

$ mkdir -p make/many/directories/at/once
$ ls -R
./make:
many</p><p>./make/many:
directories</p><p>./make/many/directories:
at</p><p>./make/many/directories/at:
once</p><p>./make/many/directories/at/once:

如果您需要了解下一个发薪日的时间,只需要输入 cal。不带任何参数时,cal 将显示当前月份的日历。cal -3 命令显示上个月、这个月和下个月的日历,而 cal 06 2009 将显示 2009 年 6 月的日历。(我的生日是那一年的某个星期一!)

复制代码

代码如下:

$ cal</p><p> November 2006
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
$ cal 06 2009</p><p> June 2009
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

因为 UNIX 具有许多命令,所以不太可能记住所有实用工具的所有选项。事实上,有时候甚至记不住实用工具的名称。

在遇到困难时,可以求助于 man。例如,要查看如何使用 man 本身,可以输入 man man。使用 man rm 和 man mv,您还可以查看有关 rm 和 mv 的解释。并且,如果清楚需要查找的主题,那么您可以使用 man -k 查找与该主题相关的 man 页面列表。

复制代码

代码如下:

$ man -k cron
cron (8) - daemon to execute scheduled commands (Vixie Cron)
crontab (1) - maintain crontab files for individual users (V3)
crontab (5) - tables for driving cron
dh_installcron (1) - install cron scripts into etc/cron.*

在本示例中,man 找出了一些实用工具的 man 页面,其中有一行描述内容中包含单词 cron。这些 man 页面中可能解释了如何使用 cron,这是一个负责系统任务调度的守护进程。

那么其中的数值代表什么含义呢?每个数值表示联机 UNIX 手册中的一个部分。第 1 部分保留用于 UNIX 用户可以在 Shell 中运行的所有命令。第 5 部分描述了一些文件格式。第 8 部分对系统管理命令进行了编目。其他的部分描述了系统调用 (2)、库调用 (3),等等。

正如您所看到的,大多数命令都会产生某类输出。大多数命令行命令使用标准输出来显示结果。但其他的一些命令则使用标准输出和标准错误,并按顺序显示处理过程和错误消息。如果您希望忽略这类输出(这是非常有价值的,因为它通常可以干预命令行中执行的操作),那么可以将输出重定向到 UNIX bit bucket,/dev/null。这些位只能进,不能出。

下面是一个简单的示例:

复制代码

代码如下:

$ ls
secret.txt
$ cat secret.txt
I am the Walrus.
$ cat secret.txt > /dev/null
$ cat socrates.txt > /dev/null
cat: socrates.txt: No such file or directory
$ cat socrates.txt >& /dev/null
$ echo Done.
Done.

如果将 cat 的标准输出重定向到 /dev/null,那么将不会显示任何内容,因为已将所有的位发送到了虚拟的“永久竖向文件”。然而,如果出现了错误,将显示发送到标准错误的错误消息。如果您希望忽略所有的输出,可以使用 >& 操作符以便将 stdout 和 stderr 丢弃。

您还可以将 /dev/null 作为一个长度为零的文件,以清空现有的文件或者创建新的空白文件:

复制代码

代码如下:

$ cat secret.txt
Anakin Skywalker is Darth Vader.
$ cp /dev/null secret.txt
$ cat secret.txt
$ echo "The moon is made of cheese!" > secret.txt
$ cat secret.txt
The moon is made of cheese!
$ cat /dev/null > secret.txt
$ cat secret.txt
$ cp /dev/null newsecret.txt
$ cat newsecret.txt
$ echo Done.

Done.顺便提一下,如果您在 Macintosh 中使用 UNIX,那么可以在一个终端窗口中尝试 open 命令。例如,如果当前工作目录中有一个名为 poodle.jpg 的文件,命令 open poodle.jpg 将启动 Preview 并打开 poodle.jpg,Preview 是 Mac OS X 中内置的图像查看器。Mac OS X open 是命令行和 Macintosh 的窗口环境之间的纽带,并且它比借助于 Finder 要快得多。

下面来总结一下!
噢!尽管这是一辆高速行驶的列车,但现在您已经做好了准备,可以更深入地探索 UNIX。您甚至了解了在不需要某些内容时,应该将其丢弃。

和以前一样,还有更多的内容需要介绍。在以后的几个月中,对话 UNIX 系列将深入研究作业控制、正则表达式(一种奇怪的方言,但并不难掌握)、如何编译从 Internet 上下载的新的实用工具,等等。

不要忘了抹点防晒油哟!