如何在Python脚本中捕获Python解释器和/或CMD.EXE的输出?
- 有没有办法从一个Python脚本中获取Python解释器的输出?
- 有没有办法从一个Python脚本中获取Windows命令提示符(CMD)的输出?
如果可以的话,我应该关注哪些库呢?
6 个回答
其实,你完全可以这样做,而且这既美妙又疯狂,甚至有点丑!
你可以用 StringIO 对象来替换 sys.stdout 和 sys.stderr,这样就能收集输出内容。
下面是一个例子,保存为 evil.py:
import sys
import StringIO
s = StringIO.StringIO()
sys.stdout = s
print "hey, this isn't going to stdout at all!"
print "where is it ?"
sys.stderr.write('It actually went to a StringIO object, I will show you now:\n')
sys.stderr.write(s.getvalue())
当你运行这个程序时,你会看到:
- 没有任何内容输出到 stdout(通常 print 的输出地方)
- 第一个写入 stderr 的字符串是以 'It' 开头的那一行
- 接下来的两行是被收集到 StringIO 对象中的内容
像这样替换 sys.stdout 和 sys.stderr 被称为猴子补丁(monkeypatching)。对于这种做法是否“被支持”,大家的看法可能不一样,这确实是一种比较丑陋的黑科技,但在我尝试处理一些外部内容时,它帮了我不少忙。
这个方法在 Linux 上测试过,Windows 上没测试过,但应该也能正常工作。如果你在 Windows 上试过,请告诉我结果!
我觉得我可以给你指个方向,回答你问题的第一部分。
1. 从一个Python脚本中捕获Python解释器的输出是否可能?
答案是“可以”,我个人比较喜欢下面这个例子,摘自于PEP 343 -- "with" 语句的文档。
from contextlib import contextmanager
import sys
@contextmanager
def stdout_redirected(new_stdout):
saved_stdout = sys.stdout
sys.stdout = new_stdout
try:
yield None
finally:
sys.stdout.close()
sys.stdout = saved_stdout
用法如下:
with stdout_redirected(open("filename.txt", "w")):
print "Hello world"
这个方法的一个好处是,它可以只在脚本执行的某一部分使用,而不是整个脚本都用。而且即使在这个上下文中出现未处理的错误,它依然有效。如果在第一次使用后以追加模式重新打开文件,你可以把结果累积到一个文件里:
with stdout_redirected(open("filename.txt", "w")):
print "Hello world"
print "screen only output again"
with stdout_redirected(open("filename.txt", "a")):
print "Hello world2"
当然,上面的内容也可以扩展到将sys.stderr
重定向到同一个或另一个文件。你也可以查看这个回答,它和这个问题相关。
如果你说的是 Python 解释器或者 CMD.exe(也就是你脚本的“父进程”),那么不,这种情况下是不可能的。在每个类似 POSIX 的系统中(现在你似乎在用 Windows,这可能有一些我不知道的特殊情况),每个进程都有三个流:标准输入、标准输出和标准错误。默认情况下(在控制台运行时),这些流都是指向控制台的,但你可以通过管道符号来重定向它们:
python script_a.py | python script_b.py
这段代码把脚本 A 的标准输出流连接到了脚本 B 的标准输入流。在这个例子中,标准错误仍然是输出到控制台的。你可以查看维基百科上关于 标准流 的文章。
如果你说的是一个子进程,你可以这样从 Python 启动它(如果你想要双向通信,标准输入也是一个选项):
import subprocess
# Of course you can open things other than python here :)
process = subprocess.Popen(["python", "main.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
x = process.stderr.readline()
y = process.stdout.readline()
process.wait()
有关管理进程的信息,可以查看 Python 的 subprocess 模块。对于通信,process.stdin 和 process.stdout 管道被认为是标准的 文件对象。
关于管道的使用,按照 lassevk 的建议,你可以这样做:
import sys
x = sys.stderr.readline()
y = sys.stdin.readline()
sys.stdin 和 sys.stdout 是上面提到的标准文件对象,定义在 sys 模块中。你可能还想看看 pipes 模块。
不过,用 readline() 来读取数据是个比较简单的方法。如果输出不是按行组织的,或者是不可预测的,你可能需要考虑 轮询,但不幸的是,这在 Windows 上是无法工作的,不过我相信还有其他的替代方案。