管道操作符把一个 UNIX? 命令连接到另一个命令,从而在命令行上创建专门的程序。但是,管道像是黑箱子,用户看不到从一个命令传递到下一个命令的数据。Pipe Viewer 可以探察管道。本文介绍如何在日常任务中使用它。
UNIX 中最巧妙最强大的功能之一是 shell。shell 比 GUI 高效得多,还可以通过编写脚本自动执行许多任务。更好的是,管道操作符可以在命令行上组装出专门的程序。管道按次序把命令连接起来,前一个命令的输出作为后一个命令的输入。
|
但是,管道有一个大缺点:它像是黑箱子。如果把命令连接在一起,那么只有序列中最后一个命令生成的输出能够反映管道的进度。当然,可以在序列中插入 tee
,还可以用 tail
查看输出文件的增长,但是这些解决方案都不完善,会把多个阶段的标准输出 (stdout
) 和标准错误 (stderr
) 混在一起。另外,这两个解决方案比较粗糙,很可能无法表明每个步骤所需的计算量。
当然,可以把复杂的序列分解为多个单独的步骤,每个步骤有自己的中间输出文件。的确,如果希望检查每个步骤的结果,分解是最理想的方法。编写一个脚本,为每个步骤生成一个数据文件,在每对步骤之间使用数据文件作为输入,以最后的文件作为最终结果。但是,这种做法浪费了命令行的灵活性。
我们需要的是一个可以嵌入命令行中的进度度量工具。理想情况下,可以对每个步骤重复使用这个工具,它应该是开放源码的,可以移植到 Linux? 和 Mac OS X 等多种 UNIX 变体上。
好了,不必再盼望了:Pipe Viewer (pv
) 就是这样的工具。它由系统管理员 Andrew Wood 编写,经过其他许多开发人员改进,前后历时四年。它提供了探察命令行管道的能力。它的 项目页面 上说,pv
“可以插入管道中两个进程之间,从而显示传递数据的速度、已经花费的时间以及剩余时间。” 更引人注目的是,可以在同一个命令行中插入多个 pv
实例以显示相对吞吐量。
本文介绍如何在 UNIX 系统上构建 pv
,以及如何在简单和复杂的命令行组合中应用它。但是,我们首先回顾一下管道连接进程的方式。
UNIX 管道:进程的连接
图 1 说明通过创建管道连接两个独立进程的步骤。
图 1. 通过创建管道连接两个进程
首先,在第一阶段,初始进程从标准输入 stdin
读取数据,把输出写到 stdout
,把错误发送到 stderr
。stdin
、stdout
和 stderr
都是文件描述符,即文件的句柄。文件句柄上的每个操作(例如 open
、read
、write
、rewind
、truncate
和 close
)影响文件的状态。
接下来,在第二阶段,初始进程创建一个管道。管道由一个队列和两个文件描述符(一个用于数据入列,另一个用于数据出列)组成。管道是先入先出 (FIFO) 数据结构。
管道本身没什么用;它的作用是连接数据生成者和数据消费者。因此,初始进程在第三阶段生成(创建)第二个进程,它作为数据消费者。
在第四阶段(假设新的进程是消费者),原来的进程把它的 stdout
替换为管道的生成者端,让刚生成的进程连接管道的消费者端,以它作为自己的 stdin
。经过这些调整之后,原来的进程(现在的生成者)执行的每个 write
操作都进入队列,然后由新进程(现在的消费者)读取。
在使用命令行管道操作符 (|
) 连接两个实用程序时,shell 就是采用第一到第四阶段这样的过程,但是 shell 为每个实用程序生成新进程,其本身并不执行作业控制。
例如,图 2 说明如何通过管道连接 find
、grep
和 wc
命令,从而寻找名称以小写字母 a 开头的所有文件并统计其数量。shell 仍然是独立的;find
是生成者,grep
作为 find
的消费者和 wc
的生成者。wc
也同时作为消费者和生成者:它消费来自 grep
的数据并向 stdout
生成输出。通常,shell 把 stdout
连接到一个终端,但是也可以把输出重定向到文件。
图 2. 使用管道连接命令
如果希望探察两个 UNIX 进程,那么要创建两个管道,重新连接每个进程的文件描述符,让它们同时作为生成者和消费者。图 3 说明覆盖这两个进程的 stdin
和 stdout
的进程间交换。
图 3. 查看两个 UNIX 进程
简单回顾管道之后,我们来看看 Pipe Viewer。
Pipe Viewer:可观察的管道
Pipe Viewer 是一个开放源码应用程序。可以下载它的源代码并从头构建这个应用程序,也可以从您的 UNIX 发行版的存储库获得二进制代码(如果有的话)。
要想从头构建,首先从 Pipe Viewer 项目页面(见 参考资料)下载最新的源代码压缩文件。到 2009 年 9 月中旬,最新版本是 1.1.4。解压压缩文件,进入刚创建的目录,输入 ./configure
,然后输入 make
和 sudo make install
。在默认情况下,构建过程会在 /usr/local/bin 中安装一个名为 pv
的可执行文件。(输入 ./configure –help
可以看到配置选项的列表)。清单 1 给出安装命令。
清单 1. Pipe Viewer 安装命令
|
要想从存储库获得 pv
,应该使用发行版的包管理程序搜索 pv
或 pipe viewer。例如,使用 Ubuntu version 9 的 APT 包管理程序执行搜索会产生以下输出:
$ apt-cache search part viewer |
接下来,使用包管理程序下载并安装包。对于 Ubuntu,命令是 apt-get install
:
$ sudo apt-get install pv |
安装完成之后,试一下 pv
。最简单的使用方法是用 pv
替代传统的 cat
实用程序,把数据提供给另一个程序并测量总吞吐量。例如,可以使用 pv
监视一个长时间的压缩操作:
$ ls -lh listings.txt |
启动这个命令时,pv
显示一个进度条并不断更新。典型的 pv
输出从左到右依次显示目前已经处理的数据量、经过的时间、吞吐量(以 MB/s 为单位)、工作进度的图形表示和数字表示以及估计的剩余时间。上面的显示表明,在工作 9 秒之后,已经处理了 109MB 中的 96.1MB,剩余数据大约为文件的 13%。
在默认情况下,pv
会显示它能够计算出值的所有状态指标。例如,如果 pv
的输入不是文件,也没有手工指定大小,进度条会从左到右移动以表示有活动,但是由于没有总大小,它无法计算出已经完成的百分比。例如:
$ ssh faraway tar cf – projectx | pv –wait > projectx.tar |
这个示例在远程机器上运行 tar
,把远程命令的输出发送给本地系统以创建 projectx.tar。因为 pv
无法计算出要传输的总字节数,它只能显示到目前为止的吞吐量、经过的时间和一个反映活动的特殊标志。只要有数据流过,就显示从左到右移动的小 “车” (<=>
)。
–wait
选项把进度的显示延迟到实际收到第一个字节时。在这里,–wait
是有意义的,因为 ssh
命令可能提示输入密码。
可以根据需要用各种标志启用不同的指标:
$ ssh faraway tar cf – projectx | pv –wait –bytes > projectx.tar |
这个命令用 –bytes
显示正在处理的字节数。其他选项是 –progress
、–timer
、–eta
、–rate
和 –numeric
。如果指定一个或多个显示选项,就会自动地禁用其他所有(未指定的)指标。
pv
有另一种简单的使用方法。–rate-limit
选项可以限制吞吐量。这个选项的参数是一个数字和一个后缀,比如 m
表示 MB/s:
$ ssh faraway tar cf – projectx | pv –wait –quiet –rate-limit 1m > projectx.tar |
这个命令隐藏所有指标 (–quiet
) 并把吞吐量限制为 1MB/s。
Pipe Viewer 的高级用法
到目前为止,示例都是使用 Pipe Viewer 的单一实例作为一对命令中的生成者或消费者。但是,也可以使用更复杂的组合。可以在同一个命令行上多次使用 pv
,但是有一些限制。具体地说,必须使用 –name
命名 每个 pv
实例,还必须使用 –cursor
启用多行模式。这两个选项的组合创建一系列带标签的输出行,每个命名的实例一行。
例如,假设希望同时单独地监视数据传输和压缩的进度。可以给前一个操作分配一个 pv
实例,给后一个操作分配另一个实例,比如:
$ ssh faraway tar cf – projectx | pv –wait –name ssh | gzip | pv –wait –name gzip > projectx.tgz |
输入密码之后,Pipe Viewer 命令生成两行进度显示:
ssh: 4.17MB 0:00:07 [ 648kB/s] [ <=> ] |
第一行的标签是 ssh
,显示传输的进度;第二行的标签是 gzip
,显示压缩的进度。因为这两个命令都不能确定要操作的字节数,所以每行上只显示累计的总数据量和活动条。
如果知道或者能够估计要操作的字节数,那么可以使用 –size
选项。添加这个选项会在进度条中提供更细粒度的信息。
例如,如果希望监视一个大型存档任务的进度,可以使用其他 UNIX 实用程序计算原文件的总大小。df
实用程序可以显示整个文件系统的统计数据,du
可以计算任意深度的层次结构的大小:
$ tar cf – work | pv –size `du -sh work | cut -f1` > work.tar |
在这里,子 shell 命令 du -sh work | cut -f1
以与 pv
兼容的格式生成工作目录的总大小。du -h
产生人习惯的表示格式,比如 17M 表示 17 MB,这种格式适合在 pv
中使用。(ls
和 df
命令也支持通过 -h
产生人可读的格式)。因为 pv
现在知道将通过管道传输的字节数,所以它可以显示真正的进度条:
700kB 0:00:07 [ 100kB/s] [> ] 4% ETA 0:02:47 |
最后,还有一种很有用的技术。除了统计字节数之外,Pipe Viewer 还可以通过计算行数 显示进度。如果指定 –line-mode
,pv
会在每次遇到换行符时更新进度条。还可以提供 –size
和预期的行数。
例如,用户经常使用 find
在大范围内寻找很小的东西,比如在大量应用程序代码中寻找使用某个系统调用的所有地方。在这种情况下,可能执行下面这样的命令:
$ find . -type f -name ‘*.c’ -exec grep –files-with-match fopen \{\} \; > results |
这个命令会找到包含字符串 fopen
的所有 C
源代码文件并输出文件名。输出收集在名为 results 的文件中。为了反映活动,在这个命令中添加 pv
:
$ find . -type f -name ‘*.c’ -exec grep –files-with-match fopen \{\} \; | pv –line-mode > results |
行模式很有意义,因为 find
等许多 UNIX 命令操作文件的元数据,而不是文件的内容。行模式很适合那些复制或压缩大型文件集的系统管理脚本。
一般情况下,只要可以度量速度,就可以在命令行和脚本中插入 Pipe Viewer。但是,可能需要发挥创造性。例如,要度量复制一个目录的速度,应该把 cp -pr
改为 tar
:
$ # an equivalent of cp -pr old/somedir new |
对于 wget
、curl
和 scp
等网络实用程序,也可以考虑使用行模式。例如,可以使用 pv
度量大小可变的上传操作的进度。因为许多网络工具可以从文件接收输入,可以使用文件的长度作为 –size
选项的参数。
结束语
Pipe Viewer 是不太为人所知的宝贵工具之一,但是一旦掌握了它,您就会发现离不开它了。您可能在平时使用命令行时发现一些需要应用 pv 的地方,但是更常用的地方是自动化脚本。不再需要盯着闪动的光标长时间等待了,现在可以通过插入 Pipe Viewer 提供实时的反馈。Pipe Viewer 让您能够监视原本隐秘的过程。
关于作者
Martin Streicher 是一位 Ruby on Rails 自由开发人员和 Linux Magazine 的前主编。Martin 毕业于 Purdue University 并获得计算机科学学位,从 1986 年起他一直从事 UNIX 类系统的编程工作。他喜欢收集艺术品和玩具。