使用子进程的python脚本,将所有输出重定向到文件

Posted

技术标签:

【中文标题】使用子进程的python脚本,将所有输出重定向到文件【英文标题】:python script using subprocess, redirect ALL output to file 【发布时间】:2015-08-12 11:30:58 【问题描述】:

我正在为不同语言的源代码的静态分析编写一些东西。由于任何东西都必须是开源的并且可以从命令行调用,我现在已经为每种语言下载了一个工具。所以我决定编写一个 python 脚本,列出项目文件夹中的所有源文件并调用相应的工具。

所以我的部分代码如下所示:

import os
import sys
import subprocess
from subprocess import call
from pylint.lint import Run as pylint


class Analyser:

    def __init__(self, source=os.getcwd(), logfilename=None):

        # doing initialization stuff
        self.logfilename = logfilename or 'CodeAnalysisReport.log'

        self.listFiles()
        self.analyseFiles()


    def listFiles(self):
    # lists all source files in the specified directory


    def analyseFiles(self):

        self.analysePythons()
        self.analyseCpps()
        self.analyseJss()
        self.analyseJavas()
        self.analyseCs()


if __name__ == '__main__':

    Analyser()

让我们看一下 C++ 文件部分(我使用 Cppcheck 来分析这些):

    def analyseCpps(self):

        for sourcefile in self.files['.cc'] + self.files['.cpp']:
            print '\n'*2, '*'*70, '\n', sourcefile
            call(['C:\\CodeAnalysis\\cppcheck\\cppcheck', '--enable=all', sourcefile])

其中一个文件(它只是一个随机下载的文件)的控制台输出是:

**********************************************************************
C:\CodeAnalysis\testproject\cpp\BiggestUnInt.cc
Checking C:\CodeAnalysis\testproject\cpp\BiggestUnInt.cc...
[C:\CodeAnalysis\testproject\cpp\BiggestUnInt.cc:18]: (style) The scope of the variable 'oldi' can be reduced.
[C:\CodeAnalysis\testproject\cpp\BiggestUnInt.cc:43]: (style) The scope of the variable 'lastbit' can be reduced.
[C:\CodeAnalysis\testproject\cpp\BiggestUnInt.cc:44]: (style) The scope of the variable 'two_to_power_i' can be reduced.
(information) Cppcheck cannot find all the include files (use --check-config for details)

第 1 行和第 2 行来自我的脚本,第 3 到 7 行来自 Cppcheck。

这就是我想要保存到我的日志文件的内容,对于所有其他文件也是如此。一个文件中的所有内容。

当然,我已经搜索过 SO 并找到了一些方法。但没有一个是完全有效的。

第一次尝试:

sys.stdout = open(self.logfilename, 'w') 添加到我的构造函数中。这使得上面显示的输出的第 1 行和第 2 行被写入我的日志文件。其余的仍然显示在控制台上。

第二次尝试:

另外,在analyseCpps 我使用:

call(['C:\CodeAnalysis\cppcheck\cppcheck', '--enable=all', sourcefile], <strong>stdout=sys.stdout</strong>)

这使我的日志文件成为:

Checking C:\CodeAnalysis\testproject\cpp\BiggestUnInt.cc...


********************************************************************** 
C:\CodeAnalysis\testproject\cpp\BiggestUnInt.cc

控制台输出为:

[C:\CodeAnalysis\testproject\cpp\BiggestUnInt.cc:18]: (style) The scope of the variable 'oldi' can be reduced.
[C:\CodeAnalysis\testproject\cpp\BiggestUnInt.cc:43]: (style) The scope of the variable 'lastbit' can be reduced.
[C:\CodeAnalysis\testproject\cpp\BiggestUnInt.cc:44]: (style) The scope of the variable 'two_to_power_i' can be reduced.

不是我想要的。

第三次尝试:

Popenpipe 一起使用。 sys.stdout 已恢复默认设置。

作为analyseCpps的前期工作,现在是:

for sourcefile in self.files['.cc'] + self.files['.cpp']:

    print '\n'*2, '*'*70, '\n', sourcefile
    p = subprocess.Popen(['C:\\CodeAnalysis\\cppcheck\\cppcheck', '--enable=all', sourcefile], stdout=subprocess.PIPE)
    p.stdout.read()

p.stdout.read() 只显示我想要的输出的最后一行(代码框 3 中的第 7 行)

第四次尝试:

使用subprocess.Popen(['C:\CodeAnalysis\cppcheck\cppcheck', '--enable=all', sourcefile], <strong>stdout=open(self.logfilename, 'a+')</strong>) 只是将一行Checking C:\CodeAnalysis\testproject\cpp\BiggestUnInt.cc... 写入我的日志文件,其余的显示在控制台上。

第五次尝试:

我使用os.system而不是subprocess.Popen,所以我的调用命令是:

os.system('C:\CodeAnalysis\cppcheck\cppcheck --enable=all %s &gt;&gt; %s' % (sourcefile, self.logfilename))

这会产生与我第四次尝试相同的日志文件。如果我直接在 Windows 控制台中键入相同的命令,结果是相同的。所以我猜这不完全是python问题,但仍然:

如果它在控制台上,肯定有办法将其放入文件中。有什么想法吗?


编辑

愚弄我。我还是个菜鸟,所以我忘记了stderr。这就是决定性信息的去向。

所以现在我有:

def analyseCpps(self):

    for sourcefile in self.files['.cc'] + self.files['.cpp']:
        p = subprocess.Popen(['C:\\CodeAnalysis\\cppcheck\\cppcheck', '--enable=all', sourcefile], stderr=subprocess.PIPE)
        with open(self.logfilename, 'a+') as logfile:
            logfile.write('%s\n%s\n' % ('*'*70, sourcefile))
            for line in p.stderr.readlines():
                logfile.write('%s\n' % line.strip())

它工作正常。


另一个编辑

根据迪迪埃的回答:

在我的构造函数中使用sys.stdout = open(self.logfilename, 'w', 0)

def analyseCpps(self):

    for sourcefile in self.files['.cc'] + self.files['.cpp']:
        print '\n'*2, '*'*70, '\n', sourcefile
        p = subprocess.Popen(['C:\\CodeAnalysis\\cppcheck\\cppcheck', '--enable=all', sourcefile], stdout=sys.stdout, stderr=sys.stdout)

【问题讨论】:

感谢您的快速cmets和答案,您很好。 相关:Redirect stdout to a file in Python? 无关:不要使用for line in p.stderr.readlines():,只需for line in p.stderr: 有效。另外,你可以写shutil.copyfileobj(p.stderr, logfile)。此外,您可以使用stderr=subprocess.STDOUT。此外,您可以 use my answer to redirect stdout at file descriptor level and merge stderr into stdout -- 在这种情况下删除 stdoutstderr 参数 -- 它们会自动处理。 【参考方案1】:

有几个问题:

您应该同时重定向 stdout 和 stderr 如果您想混合正常打印和启动命令的输出,您应该使用无缓冲文件。

类似这样的:

import sys, subprocess

# Note the 0 here (unbuffered file)
sys.stdout = open("mylog","w",0)

print "Hello"
print "-----"

subprocess.call(["./prog"],stdout=sys.stdout, stderr=sys.stdout)
print "-----"
subprocess.call(["./prog"],stdout=sys.stdout, stderr=sys.stdout)
print "-----"

print "End"

【讨论】:

这其实很酷。我将重新更改我的实现。 @seesharp:手动重定向每个子进程调用的输出似乎很脆弱,如果某些代码直接写入标准输出(os.write(1, b"won't catch me")),它会失败。更好的选择是在运行 Python 脚本的代码中重定向输出:python your_script.py &amp;&gt; log (bash) 或使用 os.dup2() 重定向(也适用于 Windows)。请参阅我的 cmets 中指向您问题的链接。 我使用 Python 2.7(没提,真丢脸)所以没有contextlib.redirect_stdout()。此外,我决定不将 every 输出重定向到文件。由于整个脚本需要相当长的时间来运行,我使用打印语句来显示进度,并且子进程的输出直接定向到我的日志文件。到目前为止它有效,所以我将保持这种方式,但我会记住你的答案以备将来使用。【参考方案2】:

你也需要重定向stderr,你可以使用STDOUT或者把文件对象传给stderr=:

from subprocess import check_call,STDOUT
with open("log.txt","w") as f:
     for sourcefile in self.files['.cc'] + self.files['.cpp']:
        check_call(['C:\\CodeAnalysis\\cppcheck\\cppcheck', '--enable=all', sourcefile],
                   stdout=f, stderr=STDOUT)

【讨论】:

【参考方案3】:

尝试将stdoutstderr 重定向到日志文件:

import subprocess

def analyseCpps(self):
     with open("logfile.txt", "w") as logfile:
         for sourcefile in self.files['.cc'] + self.files['.cpp']:
             print '\n'*2, '*'*70, '\n', sourcefile
             call(['C:\\CodeAnalysis\\cppcheck\\cppcheck',
                   '--enable=all', sourcefile], stdout=logfile,
                   stderr=subprocess.STDOUT)

在此示例中,文件名是硬编码的,但您应该能够轻松更改(更改为 self.logfilename 或类似名称)。

【讨论】:

好吧,我刚刚意识到,消息会发送到 stderr,并且正要自己输入答案:-)。所以你是对的。我将在答案中编辑工作代码。

以上是关于使用子进程的python脚本,将所有输出重定向到文件的主要内容,如果未能解决你的问题,请参考以下文章

Python子进程将数据定向到标准输入

IPC - 如何将命令输出重定向到子进程中的共享内存段

重定向子进程标准输出

Windows:具有重定向输入和输出的子进程

将子进程的标准输出重定向到 2 个或更多子进程的标准输入

在C中重定向子进程的文件输出