2.4 管道和重定向
在深入探讨shell程序设计的细节之前,我们需要先介绍一下如何才能对Linux程序(不仅仅是shell程序)的输入输出进行重定向。
2.4.1 重定向输出
读者可能已经对某些类型的重定向比较熟悉了,例如:
这条命令把ls命令的输出保存到文件lsoutput.txt中。
然而,重定向所包含的内容可比这个简单的例子所显示的要多得多。你将在第3章学习更多关于标准文件描述符的内容,现在你只需知道文件描述符0代表一个程序的标准输入,文件描述符1代表标准输出,而文件描述符2代表标准错误输出。你可以单独地重定向其中任何一个。事实上,你还可以重定向其他文件描述符,但对标准文件描述符0、1、2以外的文件描述符进行重定向的情况很少见。
上面的例子通过>操作符把标准输出重定向到一个文件。在默认情况下,如果该文件已经存在,它的内容将被覆盖。如果你想改变默认行为,你可以使用命令set -o noclobber(或set -C)设置noclobber选项,从而阻止重定向操作对一个已有文件的覆盖。你可以使用set+o noclobber命令取消该选项。你将在本章后面的内容中看到更多的set命令选项。
你可以用>>操作符将输出内容附加到一个文件中。例如:
这条命令会将ps命令的输出附加到指定文件的尾部。
如果想对标准错误输出进行重定向,你需要把想要重定向的文件描述符编号加在>操作符的前面。因为标准错误输出的文件描述符编号是2,所以使用2>操作符。当需要丢弃错误信息并阻止它显示在屏幕上时,这个方法很有用。
假设你想用kill命令在一个脚本程序里终止一个进程,那么总是存在这种可能性,即在kill命令执行之前,那个需要终止的进程就已经结束了。如果出现这种情况,kill命令将向标准错误输出写一条错误信息,并且在默认情况下,这条信息将会显示在屏幕上。通过对标准输出和标准错误输出都进行重定向,你就可以阻止kill命令向屏幕上写任何内容了。
下面的命令将把标准输出和标准错误输出分别重定向到不同的文件中:
如果你想把两组输出都重定向到一个文件中,你可以用>&操作符来结合两个输出。如下所示:
这条命令将把标准输出和标准错误输出都重定向到同一个文件中。请注意操作符出现的顺序。这条命令的含义是“将标准输出重定向到文件killouterr.txt,然后将标准错误输出重定向到与标准输出相同的地方。”如果顺序有误,重定向将不会按照你预期的那样执行。
因为可以通过返回码(我们将在本章的后面对其进行详细介绍)来了解kill命令的执行结果,所以通常并不需要保存标准输出或标准错误输出的内容。你可以用Linux的通用“回收站”/dev/null来有效地丢弃所有的输出信息,如下所示:
2.4.2 重定向输入
你不仅可以重定向标准输出,还可以重定向标准输入。例如:
很明显,在Linux下这样做意义不大,因为Linux的more命令可以接受文件名作为参数,这与Windows命令行中对应的命令不同。
2.4.3 管道
你可以用管道操作符|来连接进程。Linux与MS-DOS不同,在Linux下通过管道连接的进程可以同时运行,并且随着数据流在它们之间的传递可以自动地进行协调。举一个简单的例子,你可以使用sort命令对ps命令的输出进行排序。
如果不使用管道,你就必须分几个步骤来完成这个任务,如下所示:
一个更精巧的解决方案是用管道来连接进程,如下所示:
如果想在屏幕上分页显示输出结果,你可以再连接第三个进程more,将它们都放在同一个命令行上,如下所示:
允许连接的进程数目是没有限制的。假设你想看看系统中运行的所有进程的名字,但不包括shell本身,可以使用下面的命令:
这个命令首先按字母顺序排序ps命令的输出,再用uniq命令去除名字相同的进程,然后用grep -v sh命令删除名为sh的进程,最终将结果分页显示在屏幕上。
如你所见,与使用一系列单独的命令并且每个命令都带有自己的临时文件相比,这是一个更精巧的解决方案。但这里有一点需要引起注意:如果你有一系列的命令需要执行,相应的输出文件是在这一组命令被创建的同时立刻被创建或写入的,所以决不要在命令流中重复使用相同的文件名。如果你尝试执行如下命令:
你最终将得到一个空文件,因为你在读取文件mydata.txt之前就已经覆盖了这个文件的内容。