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

python脚本使用子流程,将所有输出重定向到文件,python,file-io,console,subprocess,cppcheck,Python,File Io,Console,Subprocess,Cppcheck,我正在为不同语言的源代码的静态分析写一些东西。由于任何东西都必须是开源的,并且可以从命令行调用,我现在下载了每种语言一个工具。因此,我决定编写一个python脚本,列出项目文件夹中的所有源文件,并调用相应的工具 我的部分代码如下所示: import os import sys import subprocess from subprocess import call from pylint.lint import Run as pylint class Analyser: def

我正在为不同语言的源代码的静态分析写一些东西。由于任何东西都必须是开源的,并且可以从命令行调用,我现在下载了每种语言一个工具。因此,我决定编写一个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()
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"

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

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

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

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

当然,我已经找到了一些方法。但没有一个能完全发挥作用

第一次尝试:

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

第二次尝试:

此外,在
分析PPS
中,我使用:

调用(['C:\codesanalysis\cppcheck\cppcheck','-enable=all',sourcefile],stdout=sys.stdout

这使我的日志文件为:

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.
不是我想要的

第三次尝试:

Popen
管道一起使用
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:\code分析\cppcheck\cppcheck','--enable=all',sourcefile],stdout=open(self.logfilename,'a+')
只需将一行
检查C:\code分析\testproject\cpp\BiggestUnInt.cc…
写入我的日志文件,其余部分显示在控制台上

第五次尝试:

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

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

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

如果它在控制台上,必须有办法将其放入文件。有什么想法吗


E D I T

我真傻。我还是个傻瓜,所以我忘了
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)

您也需要重定向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)

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

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
或类似文件名)。

有几个问题:

  • 您应该重定向stdout和stderr
  • 如果要混合普通打印和已启动命令的输出,应使用无缓冲文件
大概是这样的:

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()
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"

嗯,我刚刚意识到,这些消息会被发送到stderr,我正要自己键入一个答案:-)。所以你是对的。我将编辑答案中的工作代码。感谢您的快速评论和回答,您一切正常。相关:无关:不要对p.stderr.readlines()中的行使用
,只对p.stderr中的行使用
。此外,您还可以编写
shutil.copyfileobj(p.stderr,logfile)
。此外,还可以使用
stderr=subprocess.STDOUT
。此外,您还可以--在本例中删除
stdout
stderr
参数--它们是自动处理的。这实际上很酷。我将重新更改我的实现。@seesharp:手动重定向每个子进程调用的输出似乎很脆弱,如果某些代码直接写入stdout(
os.write(1,b“不会捕获我”)
),则会失败。更好的替代方法是重定向运行Python脚本的代码中的输出:
Python your_script.py&>log
(bash)或使用
os.dup2()
重定向(也适用于Windows)。请参阅我对您的问题的评论中的链接。我使用的是Python 2.7(没有提到,真不好意思),因此没有
contextlib.redirect\u stdout()
。此外,我决定不将每个输出重定向到一个文件。由于整个脚本需要相当长的时间来运行,我使用print语句来显示进度,并且子流程的输出直接指向我的日志文件。到目前为止,它是有效的,所以我将保持这种方式,但我会记住你的答案,为未来。