交互式Python脚本输出保存到文件中

1 投票
3 回答
2236 浏览
提问于 2025-04-15 17:13

我该如何记录一个Python脚本及其调用的所有脚本的所有活动呢?

我之前有几个Bash脚本,现在写了一个Python脚本来调用这些Bash脚本。我希望把这些脚本产生的所有输出都存储到一个文件里。

这个脚本是交互式的Python脚本,也就是说里面有raw_input的内容,所以我不能像这样用'python script.py | tee log.txt'来记录整个Python脚本的输出,因为出于某些原因,问题在屏幕上是看不到的。

这里是脚本的一部分,它调用了一个shell脚本。

    cmd = "somescript.sh"
    try:
    retvalue = subprocess.check_call(cmd, shell=True)
except subprocess.CalledProcessError:
    print ("script command has been failed")
    sys.exit("exit from script")

你觉得这里可以怎么做呢?

编辑

根据Alex的回答,有两个子问题:

  1. 如何把用户对问题的回答也记录到输出文件里呢?比如在这一行ok = raw_input(prompt)中,用户会被问到问题,我希望把这个回答也记录下来。

  2. 我读过关于Popen和communicate的内容,但没有使用,因为它会把数据缓存在内存中。这里的输出量很大,我还需要同时处理标准错误和标准输出。你知道用Popen和communicate的方法能否处理这个问题吗?

3 个回答

1

Python有一个叫做跟踪模块的东西,名字是 trace。使用方法是:python -m trace --trace file.py

1

如果你想要捕捉任何脚本的输出,在类Unix系统上,你可以把标准输出和错误输出重定向到一个文件里:

./script.py >> /tmp/outputs.txt 2>> /tmp/outputs.txt

如果你想要获取脚本做的所有事情,而不仅仅是它们打印的内容,那么Python的trace模块是无法追踪那些由你运行的外部脚本所做的事情的。要想追踪一个程序所做的每一个动作,可能需要像DTrace这样的工具,前提是你的系统支持它。(OS X的Instruments就是基于DTrace的)

7

让Python的print同时输出到终端和文件其实并不难:

>>> import sys
>>> class tee(object):
...   def __init__(self, fn='/tmp/foo.txt'):
...     self.o = sys.stdout
...     self.f = open(fn, 'w')
...   def write(self, s):
...     self.o.write(s)
...     self.f.write(s)
... 
>>> sys.stdout = tee()
>>> print('hello world!')
hello world!
>>> 
$ cat /tmp/foo.txt
hello world!

这个方法在Python 2和Python 3中都可以使用。

如果你想让子命令的输出也能这样处理,不要使用

retvalue = subprocess.check_call(cmd, shell=True)

这样会让cmd的输出直接到它的“标准输出”里,而是要自己抓取并重新输出,方法如下:

p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
so, se = p.communicate()
print(so)
retvalue = p.returncode

假设你不在乎标准错误(只关心标准输出),而且cmd的输出量比较小(因为.communicate会把这些数据存储在内存中)——如果这两个假设不符合你的需求,调整起来也很简单。

编辑:提问者现在在这个回答下的长评论中澄清了需求:

  • 如何让输出文件中也记录下问题的答案?例如在行 ok = raw_input(prompt) 中,用户会被问到问题,我希望答案也能被记录。

可以使用这样的函数:

def echoed_input(prompt):
    response = raw_input(prompt)
    sys.stdout.f.write(response)
    return response

而不是在你的应用代码中直接使用raw_input(当然,这个函数是专门为配合上面提到的tee类而写的)。

  • 我了解过Popen和communicate,但没有使用,因为它会把数据存储在内存中。这里输出量很大,我还需要同时处理标准错误和标准输出。你知道用Popen和communicate方法能否处理这个吗?

只要你得到超过内存能承受的输出(和标准错误),比如说最多几GB,communicate是可以的,这取决于你使用的机器类型。

如果这个假设成立,可以把上面的代码改成:

p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, 
                     stderr=subprocess.STDOUT)
so, se = p.communicate()
print(so)
retvalue = p.returncode

也就是说,只需将子命令的标准错误重定向到标准输出中。

如果你确实需要担心几GB(或其他)的数据涌入,那么

p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, 
                     stderr=subprocess.STDOUT)
for line in p.stdout:
  sys.stdout.write(p)
p.wait()
retvalue = p.returncode

(这个方法一次获取并输出一行)可能更合适(这取决于cmd是否不期待任何来自其标准输入的内容,当然……因为如果它确实期待什么,那就会出现问题,事情就变得复杂了;-)。

撰写回答