子进程模块无法运行命令。

4

我正在尝试在一组文件上执行Google的cpplint.py,并将结果收集到一个日志文件中。然而,我还没有成功使用subprocess模块。这是我的当前代码:

import os, subprocess

rootdir = "C:/users/me/Documents/dev/"
srcdir = "project/src/"

with open(rootdir+srcdir+"log.txt", mode='w', encoding='utf-8') as logfile:
    for subdir, dirs, files in os.walk(rootdir+srcdir):
        for file in files:
            if file.endswith(".h") or file.endswith(".cpp"):
                filewithpath=os.path.join(subdir, file)
                cmd=['c:/Python27/python.exe','C:/users/me/Documents/dev/cpplint.py','--filter=-whitespace,-legal,-build/include,-build/header_guard/', filewithpath]               
                output = subprocess.check_output(cmd)
                logfile.write(output.decode('ascii'))

尝试运行上面的代码会抛出一个错误:

  File "C:\Python32\lib\site.py", line 159
    file=sys.stderr)
        ^ SyntaxError: invalid syntax Traceback (most recent call last):   File "C:\Users\me\Documents\dev\project\src\verifier.py", line 19, in <module>
    output = subprocess.check_output(cmd)   File "C:\Python32\lib\subprocess.py", line 511, in check_output
    raise CalledProcessError(retcode, cmd, output=output) subprocess.CalledProcessError: Command '['c:/Python27/python.exe', 'C:/users/me/Documents/dev/cpplint.py', '--filter=-whitespace,-legal,-build/include,-build/header_guard/', 'C:/users/me/Documents/dev/project/src/aboutdialog.cpp']' returned non-zero exit status 1

如果我用更简单的东西替换cmd,比如:

cmd=['C:/WinAVR-20100110/bin/avr-gcc.exe','--version']

然后脚本按预期运行。

我还尝试使用单个命令字符串而不是作为cmd的字符串列表,但结果相同。 在调试代码时,我从调试器中复制了已转换为命令行命令的字符串列表,并在Windows命令行中运行它,命令正常运行。

运行我的脚本的Python解释器是Python 3.2。 欢迎任何提示。


在使用单个命令字符串时,您是否使用了标志shell=True? - fransua
无论我使用shell=True、shell=False还是根本不定义它,结果都是相同的。 - Manjabes
我在想是否这种使用Python 2.6解释器的方式是好的。使用“subprocess”确实是选择版本的一种方法,但是否有其他方法可以访问此Python模块的有用功能呢? - Joël
有趣的是,原始的SyntaxError所指的site.py模块处理导入包,这是一个遇到这种错误的奇怪地方。 - Manjabes
使用混合的Python版本(cmd = ['c:/Python27/python.exe' ...])是否会导致问题?当我使用一个虚拟的cpplint.py(只打印“hello”)运行时,它没有出现语法错误,但是停止并显示“返回非零退出状态2”。 - Dave
3个回答

11

看起来 cpplint.py 仅仅是以非零返回代码退出了 - 如果它在检查源文件时发现错误或“污点”等情况,它可能会这样做。

请参阅 subprocess.check_output 的文档。请注意,如果执行的命令返回一个非零退出代码 ,则会引发 subprocess.CalledProcessError

您可以通过监视 CalledProcessError 来解决此问题,例如:

try:
    output = subprocess.check_output(cmd)
except subprocess.CalledProcessError as e:
    # ack!  cpplint.py failed... report an error to the user?

编辑

SyntaxError 似乎是关键,可能是由于将 C:\Python32\lib 添加到了 PYTHONPATH 中(无论是显式添加还是作为当前工作目录)。Python 解释器(自大约 1.5.2 左右)在启动时会自动运行 import site。因此,当这种情况发生且您的脚本尝试执行以下操作时:

c:/Python27/python.exe C:/users/me/Documents/dev/cpplint.py ...

如果使用Python 2.7解释器,它会首先找到C:\Python32\lib\site.py,并尝试加载该文件,而不是位于C:\Python27\lib\site.py的文件(大概率是Python 2 的 site.py)。问题在于Python 3的site.py与Python 2不兼容,因此由subprocess.check_output启动的进程在运行cpplint之前即失败了,导致CalledProcessError的传播。

解决方法是确保Python 2和Python 3分别拥有其自己的“PYTHONPATH”。换句话说,在运行Python2解释器时,请确保C:\Python32\lib不在PYTHONPATH搜索路径中。

在您的情况下,一种方法是在启动进程时设置显式环境变量,例如:

python2_env = {"PYTHONPATH": "path/to/python2/stuff:..."}
output = subprocess.check_output(cmd, env=python2_env)

我尝试捕获CalledProcessError和SyntaxError,但没有成功 :( - Manjabes
1
@Manjabes:SyntaxError很奇怪。可以尝试一下:运行python -c“import site”(其中'python'是你用来运行你的脚本的Python 3.2可执行文件,而不是cpplint),看看是否会出现SyntaxError。如果是这样,那么你的python3安装可能出了问题? - bjlaub
1
@Manjabes:我已经能够通过使用Python 3.2.2 libs中的site.py文件并使用Python 2.x解释器来重复SyntaxError。所以...我想知道这是否是你问题的根源?如果您使用c:/Python27/python.exe而不是Python3运行您的脚本会发生什么? - bjlaub
此外:当您运行此代码时,您的当前工作目录是什么?如果您在C:\Python32\lib(出于某种原因),python27将在尝试自动导入site模块时失败,因为它会在系统级别之前在工作目录中找到一个site.py文件。请注意,C:\Python32\lib\site.py使用的语法与Python2不兼容。 - bjlaub
是的,就是这样。我通常使用3.2解释器作为我的默认Python,并将其放在我的PATH中(以及其子目录在我的PYTHONPATH中)。在为子进程设置修改后的环境之后,它可以工作!如果您能将您的评论重新发布为答案,我会接受它。 - Manjabes
在Windows上,您必须包含systemroot变量才能使其正常工作。例如: python2_env = {"PYTHONPATH": "path/to/python2/stuff:..." , "SystemRoot":os.environ["SystemRoot"]}请参阅subprocess文档 - Tobber

1

我是通过将 def main() 替换为以下内容来完成的(我还编辑了 errorfunction 以获得一个适当的 csv 文件):

errorlog = sys.stderr
sys.stderr = open("errorlog.csv","w")
sys.stderr.write("File;Line;Message;Category;Confidence\n")
for filename in filenames:
  ProcessFile(filename, _cpplint_state.verbose_level)
_cpplint_state.PrintErrorCounts()
sys.exit(_cpplint_state.error_count > 0)
sys.stdout = errorlog
sys.stderr.close()

1

我想请你先运行这个

pipe = subprocess.Popen([cmd, options],stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
stdout, stderr = pipe.communicate()

您将会知道背后的错误是什么,因为只有退出代码是非零时才会引发CalledProcessError。


1
标准错误(stderr)为空,标准输出(stdout)与我原来问题中的第一个错误一样:File "C:\Python32\lib\site.py", line 159 file=sys.stderr) ^ SyntaxError: invalid syntax - Manjabes
你确定要把“--filter=-whitespace,-legal,-build/include,-build/header_guard/”作为单个参数传递给程序,还是它们是多个参数? - avasal
即使我完全删除--filter参数,结果仍然是一样的。 - Manjabes
1
--filter=-whitespace','-legal','-build/include','-build/header_guard/' ... 这样所有的参数都会被视为不同的,而不是单个的。 - avasal
是的,我明白,但我试图调用的脚本期望将-whitespace、-legal等作为--filter参数的一部分。无论如何,正如我所说,如果我完全从列表中删除“--filter...”,那也无法消除错误。 - Manjabes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接