UNIX用户已经对标准输入、标准输出和标准错误的概念熟悉了。这一节是为其它不熟悉的人准备的。
标准输出和标准错误(通常缩写为 stdout 和 stderr)是建立在每个UNIX系统内的管道(pipe)。当你 print 某东西时,结果输出到 stdout 管道中;当你的程序崩溃并打印出调试信息时(象Python中的错误跟踪),结果输出到 stderr 管道中。通常这两个管道只与你正在工作的终端窗口相联,所以当一个程序打印输出时,你可以看到输出,并且当一个程序崩溃时,你可以看到调试信息。(如果你在一个基于窗口的Python IDE系统上工作,stdout 和 stderr 缺省为“交互窗口”。)
>>> for i in range(3): ... print 'Dive in' Dive in Dive in Dive in >>> import sys >>> for i in range(3): ... sys.stdout.write('Dive in') Dive inDive inDive in >>> for i in range(3): ... sys.stderr.write('Dive in') Dive inDive inDive in
正如我们在例 3.28中看到的,我们可以使用Python内置的 range 函数来创建简单的计数循环,即重复某物一定的次数。 | |
stdout 是一个类文件对象;调用它的 write 函数会打印出任何给出的字符串。事实上,这就是 print 函数真正所做的;它会在正打印的字符串后面加上回车换行符,并调用sys.stdout.write。 | |
在最简单的例子中,stdout 和 stderr 将它们的输出发送到同一个地方:Python IDE,或终端(如果你正从命令行运行Python)。象 stdout,stderr 并不为你增加回车换行符;如果需要,要自已加上。 |
stdout 和 stderr 都是类文件对象,就象我们在提取输入源中所讨论的一样,但它们都是只写的。它们没有 read 方法,只有 write。然而,它们的确是类文件对象,并且你可以将任意文件对象或类文件对象赋给它们来重定向输出。
[f8dy@oliver kgp]$ python2 stdout.py Dive in [f8dy@oliver kgp]$ cat out.log This message will be logged instead of displayed
如果你还没有这样做,你可以下载本书中用到的本例和其它例子。
#stdout.py import sys print 'Dive in' saveout = sys.stdout fsock = open('out.log', 'w') sys.stdout = fsock print 'This message will be logged instead of displayed' sys.stdout = saveout fsock.close()
重定向 stderr 完全以相同的方式进行,用 sys.stderr 代替 sys.stdout。
[f8dy@oliver kgp]$ python2 stderr.py [f8dy@oliver kgp]$ cat error.log Traceback (most recent line last): File "stderr.py", line 5, in ? raise Exception, 'this error will be logged' Exception: this error will be logged
如果你还没有这样做,你可以下载本书中用到的本例和其它例子。
#stderr.py import sys fsock = open('error.log', 'w') sys.stderr = fsock raise Exception, 'this error will be logged'
另一方面,标准输入是只读文件对象,同时它表示从前面某个程序的数据流入这个程序。这一点可能对典型的Mac OS用户可能没什么意义,或者甚至是对Windows用户也是如此,除非你更习惯在MS-DOS命令行下工作。它的工作方式是:你可以在单个文件中构造一个命令行的链,这样一个程序的输出成为链中下一个程序的输入。第一个程序简单地输出到标准输出(本身不需要任何特别的重定义,只是执行正常的 print 什么的),同时下个程序从标准输入读入,操作系统会小心地将一个程序的输出连接到下一个程序的输入。
[f8dy@oliver kgp]$ python2 kgp.py -g binary.xml 01100111 [f8dy@oliver kgp]$ cat binary.xml <?xml version="1.0"?> <!DOCTYPE grammar PUBLIC "-//diveintopython.org//DTD Kant Generator Pro v1.0//EN" "kgp.dtd"> <grammar> <ref id="bit"> <p>0</p> <p>1</p> </ref> <ref id="byte"> <p><xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/>\ <xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/></p> </ref> </grammar> [f8dy@oliver kgp]$ cat binary.xml | python2 kgp.py -g - 10110001
正如我们在接触中看到的,这样会打印出8个随机比特(0 或 1)的字符串。 | |
这样会简单地打印出 binary.xml 的全部内容。(Windows用户应该使用 type 代替 cat。) | |
这样会打印 binary.xml 的内容,但是“|”字符,叫做管道符,表示输出内容不会打印到屏幕上。相反,它们成为下个命令(在本例中调用我们的Python脚本)的标准输入。 | |
我们没有指定一个模块(象 binary.xml),而是指定“-”,这会让我们的脚本从标准输入而不是从磁盘上的一个特别文件中装入语法。(在下个例子中有更多关于它是如何发生的内容。)这样效果同第一个语法(我们直接指定语法文件名)是一样的,但它考虑了这里的扩展的可能性。不只是简单地执行 cat binary.xml,我们可以运行一个可以动态生成语法的脚本,然后可以将它通过管道输入到我们的脚本中。语法可以来自任何地方:数据库,或某个语法生成元脚本什么的。要点就是我们完全不必修改我们的 kgp.py 脚本就可以同任何这种功能进行合并。我们要做的只是能够从标准输入中接收语法文件,并且我们可以将所有其它的逻辑分散到另一个程序中。 |
那么当语法文件是“-”时我们的脚本是如何中从标准输入读入的呢?没什么神秘的,就是编码。
def openAnything(source): if source == "-": import sys return sys.stdin # try to open with urllib (if source is http, ftp, or file URL) import urllib try: [... 略 ...]
这是来自 toolbox.py 中的 openAnything 函数,我们在提取输入源中讨论过的。我们所做的全部是在函数的开始处添加三行代码,用来检查是否 source 是“-”,如果是,我们返回sys.stdin。实际上,就是这样!记住,stdin 是一个带有 read 方法的类文件对象,所以我们代码的其余部分(在 kgp.py 中,我们调用 openAnything 的地方)一点没有改变。 |