如何在Python脚本中捕获Python解释器和/或CMD.EXE的输出?

10 投票
6 回答
11022 浏览
提问于 2025-04-11 00:16
  1. 有没有办法从一个Python脚本中获取Python解释器的输出?
  2. 有没有办法从一个Python脚本中获取Windows命令提示符(CMD)的输出?

如果可以的话,我应该关注哪些库呢?

6 个回答

3

其实,你完全可以这样做,而且这既美妙又疯狂,甚至有点丑!

你可以用 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 上试过,请告诉我结果!

6

我觉得我可以给你指个方向,回答你问题的第一部分。

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重定向到同一个或另一个文件。你也可以查看这个回答,它和这个问题相关。

10

如果你说的是 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 上是无法工作的,不过我相信还有其他的替代方案。

撰写回答