了解有关 UNIX shell 的基础知识,学习如何使用命令行将有限的 UNIX 实用工具组合成无穷的数据转换。
UNIX® 系统中一项最奇特和突出的特性就是其命令行功能。您只需输入包含一定逻辑关系的少量文本,即可使用命令行来将有限的 UNIX 实用工具组合成无限的即时可用的数据转换。
例如,要在当前工作目录下的文件夹层次结构中查找独特文件名的列表,您可在 shell 提示符下输入以下命令:
find . -type f -print | sort | uniq
|
该命令行中组合了三种不同的实用工具:
-
find 对指定目录进行深度搜索,在本例中,是对从 . 或 点(代表当前工作目录)开始的文件系统进行搜索并生成满足给定条件的所有项的名称。这里,-type f 告诉 find 只查找文本文件。
-
sort ,顾名思义,将对列表进行处理并生成按字母排序的新列表。
-
uniq (读做 “unique”),扫描列表,比较列表中的相邻元素,以去除任何重复项。例如,假设您具有以下列表:
Groucho
Groucho
Chico
Chico
Groucho
Harpo
Zeppo
Zeppo
|
uniq 可将列表精简为:
Groucho
Chico
Groucho
Harpo
Zeppo
|
但是,如果首先对 Marx Brothers 的初始列表进行排序(在连续运行中对多次出现的名称进行重排),运行 uniq 会生成以下结果:
Chico
Groucho
Harpo
Zeppo
|
要了解 find 、sort 和 uniq 的更多扩展特性,请参阅您的 UNIX 系统中每种实用工具的 man 页。
独立使用 find 时,总是以文件系统的内容作为输入数据。但是 sort 和 uniq 则需要从标准输入设备 (stdin) 请求数据输入。多数情况下,您会使用键盘作为 stdin:例如,您需要输入要排序的数据行。
默认情况下,find 在标准输出设备(stdout,通常是您的终端窗口)上打印结果。sort 和 uniq 都将输出打印到 stdout。
为了说明 stdin 和 stdout,您可在终端窗口中输入以下文本(假设前面的百分号 (% ) 为您的 shell 提示符):
% sort
mustache
horn
hat
Control-D
|
sort 从 stdin 读取您输入的三行文本,并对其进行排序,然后将结果写出到 stdout。图 1 所示为从命令行运行 sort 和多数 UNIX 命令行实用工具的示意图。
某些实用工具,例如 find 并不从 stdin 读取内容。它们是从系统资源(例如文件系统或系统内核)读取需要处理的数据,然后将结果写入到 stdout。要直观查看 find 的工作方式,请参见以下的图 2。
除了使用 stdin 和 stdout 之外,UNIX 命令还将生成的错误消息输出到一种特殊出口以便进行诊断,该出口通常并不是强制的。此出口称为标准错误设备(通常简称为 stderr)。图 3 所示为运行实用工具的简单命令行。
如图 3 中所示,多数 UNIX 命令从终端读取输入,将结果发送到终端,并将错误打印到终端上。默认情况下,除非另行指定,您的终端既是 stdin 的数据源,也是 stdout 和 stderr 的输出目标。
不过,您可更改 stdin 的源以及 stdout 和 stderr 的目标。您可强制 stdin 从文本文件、设备(比如连接到计算机的探头)或网络连接中进行读取。类似地,您可将输出结果发送到文件、设备或网络连接。在 UNIX 中,所有资源都被视作文件,因此某种源或目标很容易作为另外的源或目标而被接受或产生。
更改进程数据的源和目标被称为重定向。您可重定向 stdin 以从文件或其他源读取数据,还可分别对 stdout 和 stderr 重定向以将数据写到终端窗口之外的其他位置。在许多情况下,如前面所列出的初始 find 命令中,您还可重定向实用工具以从其他工具接收和为它们产生所需的数据。这就是管道 (| ) 的用途。您可在命令中通过管道来生成进程链路,即将某条命令的数据发送到下一条命令,这类似于通过铜管将水从热水器传输到洗手池中。
图 4 所示为 find . -type f -print | sort | uniq 命令的示意图。
find 命令的 stdout 成为 uniq stdin,然后 uniq 的 stdout 又成为 sort 的 stdin。最后,sort 将结果打印到其标准输出设备,即所连接的终端窗口上。这些命令的 stderr 未进行重定向,因此所有三个实用工具都会将错误消息打印到终端上。(来自三个实用工具的错误消息会混在一起,但保持正确的顺序。)
如有需要,您可进一步扩展管道,将 uniq 的输出重定向到另外的实用工具。这只需使用另一个管道即可对转换进行扩展。例如,您可在命令之后附加 | less 以使用 less 对输出结果进行分页,或者您可添加 | wc -l 以统计独特文件名的数目。(wc 为 word count 的首字母缩写,wc 可统计字符、单词和行数。)
此外,您还可使用 > 来将整个命令序列的输出结果保存到一个文件中(这将覆盖现有的文件内容)。您可使用 >> 以将命令输出结果附加 到现有文件之后(如果文件不存在,则创建新文件)。
另一个有用的重定向是 < 。图 5 所示为如何重定向 stdin 以从文件中进行读取。命令 sort 从指定文件中读取单词列表并按字母顺序进行排序。
您常常会需要捕获 stdout 和 stderr。例如,如果您正在运行大型的数据挖掘任务,则可能要检查执行过程中的中间输出以及可能出现的任何错误。您可使用重定向语法的一些变种来实现该功能:|& , >& , >>& 可分别对 stdout 和 stderr 实现管道、创建、附加功能。图 6 所示为如何将 stdout 和 stderr 合并到单一的输出流。
包括 Bourne shell (bash ) 和 Korn shell (ksh ) 在内的多数现代 UNIX shell 都支持这里提到的重定向功能,尽管在这些 shell 中具体语法可能存在细微差别。(请查看您的 shell 文档以了解详细信息)。
重定向中的多数操作符在所有的 UNIX shell 中已经连续使用了至少 25 年。但是,多数 shell 并没有提供新的特性或采用新方式来应用重定向。例如,多数 shell 只能将输入重定向到单个文件,因此您必须使用如 tee 等实用工具来输出到多个目标。(类似于水管工人使用的 T 型管 (Tee),tee 只支持单个或两个输出。)这里提供一个使用 bash 作为 shell(命令行解释器)的示例:
bash$ ls
tellme
bash$ cat tellme
echo Your current login, working directory, and system are...
whoami
pwd
systemname
bash$ bash < tellme |& tee log
Your current login and working directory are...
strike
/home/strike
bash: systemname: command not found
bash$ ls
tellme log
bash$ cat log
Your current login and working directory are
strike
/home/strike
bash: systemname: command not found
|
尽管 UNIX shell 具有较高的专用性,且通常使用键盘进行交互,但某些 shell 如 bash 等也能从文件读取输入内容。(实际上,stdin 也是一种文件。)在前面的片段中,短语 bash < commands 告诉 bash 执行在文件 tellme 中找到的命令列表。短语 |&tee log 将 bash 的 stdout 和 stderrto 通过管道重定向到 tee 实用工具,后者将其 stdin 打印到 stdout 和 文件 log 中。
但是,如果您打算使用 bash 来处理多个文件,该怎么办呢?cat file1 file2 file3 | bash 是一种可行的方法,这也许也是唯一的一种方法,因为在 bash 中并不支持如 bash < file1 < file2 < file3 的语法。
而且,bash 无法将输出重定向到多个目标。例如,您可从 bash 命令行中输入指令 bighairyscript > ~/log | mail -s "Important stuff" team 。
但在相对较新的 shell 如 Z shell(zsh ;请参阅参考资料)中,可以在同一命令行内处理多个输入和输出。例如,使用以下命令可将 stdout 保存到名为 log 的文件中并通过电子邮件发送给您自己:
zsh% bash < tellme > log | mail -s "Who you are" 'whoami'
bash: line 4: systemname: command not found
zsh%
Your current login, working directory, and system are...
strike
/home/strike
|
(短语 “whoami” 运行命令 whoami 并将该命令的结果插入到短语所在位置。它类似于在运行命令行的其他部分之前先运行一条短小 shell 命令。)
现在我们对上一条命令从左向右进行分析。bash 命令创建文件 log 并将在 tellme 文件中找到的命令的 stdout 发送给您自己。由于 stderr 没有通过 > 或管道进行定向,因此错误消息将被打印到 stdout。命令 为另一个 Z shell 快捷方式,它与 cat 相同。(因此,命令 > file 等同于 cat > file 。)
Z shell 还可处理多个输入重定向。Z shell 命令行 cat < file1 < file2 < file3 等同于 cat file1 file2 file3 。显然,原有语法较后者更加繁琐,总的来说,多个 stdout 重定向要更加常用的多。但是,如果您要运行的实用工具不接受多个输入参数,则可使用 Z shell 的多个输入重定向。
Z shell 中还具有其他新特性,包括更好的 globbing(通配符匹配)、先进的模式匹配和扩展的命令自动完成系统,从而减少您在命令行中的字符输入。本系列中的后续两篇文章将进一步探讨 Z shell。
通过一些功能强大的命令行组合,能明显提高您的工作效率。这些命令可以在所有的 shell 中工作,而不仅仅是 zsh 。
-
使用 tar 为任何目录创建包括符号链接在内的完整副本:
tar cf - /path/to/original | \
(mkdir -p /path/to/copy; cd /path/to/copy; tar xvf -)
|
第一个 tar 命令将目录 /path/to/original 进行归档并将归档文件写到 stdout,创建 (c ) 选项后面使用的连字符 (- ) 表示 stdout。括号中的命令为一个 subshell:subshell 中的命令不会影响当前 shell 的环境。mkdir -p 创建指定目录,包括任何需要创建的中间目录;cd 命令则切换到新目录。第二个 tar 命令从 stdin 读取归档文件并进行展开,展开 (x ) 选项后面使用的连字号表示 stdin。
-
要在保存命令序列的 stdout 同时进行查看,可使用 less -O file 。-O 选项会将 stdin 复制到指定的 file 中。如下例所示:
sort /etc/aliases | less -Osorted
|
-
如果目录中包含数千个文件,则您的 shell(包括 zsh ,取决于文件数目及其名称)可能无法使用通配符匹配来列举出所有文件,因为命令行通常具有一定的字符数限制。因此,类似以下 shell 脚本:
可能会执行失败。(当超出允许命令行长度时,您可能看到类似 Line length exceeded 的消息。)如果出现此类错误,可使用管道 xargs 实用工具。xargs 命令可从管道中读取数据并为每行读取内容运行指定命令。
例如,如果您要查找服务器上的所有引用 的网页,可使用以下命令行:
% find / -name '*html' -print \
| xargs grep -l '' \
| less -Opages
|
xargs 接收来自 find 的文件名并重复运行 grep -l 以处理每个文件,而不论有多少个文件。(grep -l 在发现一个匹配项之后即打印文件的名称并停止在该文件中的进一步匹配。) less 允许您对结果进行分页并将列表保存在文件指定页中。命令结果为包含字符串“”的文件名列表。
|
|
本文为您介绍了有关 UNIX shell 的基础知识。后续文章将更深入地讨论命令行工具的更多内容以及使用技巧。通过灵活运用 UNIX 命令行功能,可实现从文件系统到整个本地局域网的所有信息和系统管理的透明化。
祝你好运!
学习
获得产品和技术
讨论
|
|
|
Martin Streicher 目前担任 Linux Magazine 的主编。他毕业于普渡大学并获得计算机科学硕士学位,从 1982 年起他一直从事 UNIX-like 系统的编程工作,他使用的编程语言包括 Pascal、C、Perl、Java 以及近期的 Ruby 语言。 | |