交互式Python脚本输出存储在某个文件中

yar*_*art 1 python logging

如何记录Python脚本完成的所有活动以及从中调用的所有脚本?

我有几个Bash脚本,但现在编写了一个调用所有这些Bash脚本的Python脚本.我想将这些脚本生成的所有输出存储在某个文件中.

该脚本是交互式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")
Run Code Online (Sandbox Code Playgroud)

您认为在这里可以做些什么?

编辑

基于Alex答案的两个子问题:

  1. 如何对存储在输出文件中的问题做出答案?例如,在线ok = raw_input(prompt)上将询问用户该问题,并且我也希望记录答案.

  2. 我读到有关Popen的信息并且没有使用,因为它将数据缓冲在内存中.这里输出量很大,我也需要关注标准输出的标准误差.你知道这是否可以用Popen和沟通方法处理?

Ale*_*lli 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!
Run Code Online (Sandbox Code Playgroud)

这应该适用于Python 2和Python 3.

要类似地指导子命令的输出,请不要使用

retvalue = subprocess.check_call(cmd, shell=True)
Run Code Online (Sandbox Code Playgroud)

cmd我们的输出转到它的常规"标准输出",而是自己抓取并重新发出它,如下所示:

p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
so, se = p.communicate()
print(so)
retvalue = p.returncode
Run Code Online (Sandbox Code Playgroud)

假设您不关心标准错误(仅标准输出)并且输出量cmd相当小(因为.communicate内存中的数据缓冲区) - 如果任何一个假设与您的确切不对应,则很容易调整想.

编辑:OP现在已经澄清了对这个答案的长期评论中的规范:

  • 如何对存储在输出文件中的问题做出答案?例如,在线ok = raw_input(提示),将询问用户该问题,我也想记录答案.

使用如下功能:

def echoed_input(prompt):
    response = raw_input(prompt)
    sys.stdout.f.write(response)
    return response
Run Code Online (Sandbox Code Playgroud)

而不仅仅是raw_input在您的应用程序代码中(当然,这是专门为与tee上面显示的类合作而编写的).

  • 我读到有关Popen的信息并且没有使用,因为它将数据缓冲在内存中.这里的输出量很大,我也需要关注标准输出的标准误差.你知道这是否可以用Popen和沟通方法处理?

communicate只要你没有获得更多的输出(和标准错误),而不是舒适地适合内存,就好了,根据您使用的机器类型,最多可以说几千兆字节.

如果满足这个假设,只需将以上内容重新编码为:

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

即,只需重定向子命令的stderr以混入其stdout.

如果你不得不担心来自你的千兆字节(或其他),那么

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
Run Code Online (Sandbox Code Playgroud)

(它获取并发出一条线在同一时间)可优选(这取决于cmd不是从它的标准期待任何输入,当然......因为,如果它期待任何东西,它不会得到它,问题开始变得具有挑战性;-).