Я пишу что-то для статического анализа исходного кода на разных языках. Поскольку все должно быть с открытым исходным кодом и вызываться из командной строки, я загрузил по одному инструменту для каждого языка. Поэтому я решил написать скрипт на 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], 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
с pipe
. 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()
показывает только последнюю строку желаемого вывода (строка 7 в поле кода 3)
Четвертая попытка:
Использование subprocess.Popen(['C:\CodeAnalysis\cppcheck\cppcheck', '--enable=all', sourcefile], stdout=open(self.logfilename, 'a+'))
просто записывает одну строку Checking C:\CodeAnalysis\testproject\cpp\BiggestUnInt.cc...
в мой лог-файл, остальное отображается в консоли.
Пятая попытка:
Вместо subprocess.Popen
я использую os.system
, поэтому моя вызывающая команда:
os.system('C:\CodeAnalysis\cppcheck\cppcheck --enable=all %s >> %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)
for line in p.stderr.readlines():
, работает толькоfor line in p.stderr:
. Также вы можете написатьshutil.copyfileobj(p.stderr, logfile)
. Кроме того, вы можете использоватьstderr=subprocess.STDOUT
. Кроме того, вы можете использовать мой ответ для перенаправления stdout на уровне файлового дескриптора и объединить stderr с stdout -- удалить параметрыstdout
,stderr
в этом случае -- они обрабатываются автоматически. - person jfs   schedule 13.08.2015