子进程模块无法执行命令

5 投票
3 回答
41092 浏览
提问于 2025-04-17 04:57

我想在一组文件上运行谷歌的 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=['C:/WinAVR-20100110/bin/avr-gcc.exe','--version']

那么这个脚本就能正常工作了。

我还尝试过用一个完整的命令字符串来代替字符串列表作为命令,但结果还是一样。

在调试代码时,我从调试器里复制了那个字符串列表转换成的命令行命令,然后在 Windows 命令行里运行,结果是正常的。

我运行这个脚本的 Python 版本是 3.2。任何建议都非常感谢。

3 个回答

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这个错误。

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 3 的 site.py 包含了与 Python 2 不兼容的语法,因此通过 subprocess.check_output 启动的进程在运行 cpplint 之前就因为语法错误失败了,这就导致了 CalledProcessError 的出现。

解决方案?确保 Python 2 有一个真正的 Python 2 的 "PYTHONPATH",Python 3 也是如此!换句话说,确保在运行 Python 2 解释器时,C:\Python32\lib 不在 PYTHONPATH 的搜索路径中。

在你的情况下,可以通过在启动进程时设置一个明确的环境来做到这一点,比如:

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

撰写回答