执行子进程失败

19 投票
2 回答
60529 浏览
提问于 2025-04-15 16:31

我尝试用Python调用一个进程,并传递几个参数。直接执行这个批处理文件对我来说没问题,但把它转换成Python代码让我很抓狂。下面是这个批处理文件的内容:

"C:\Program Files\bin\cspybat" "C:\Program Files\bin\armproc.dll" "C:\Program Files\bin\armjlink.dll" "C:\Documents and Settings\USER\Desktop\CAL\testing\Verification\FRT\Code\TC1\Output\Genericb\Debug\Exe\Gen.out" --download_only --backend -B "--endian=little" "--cpu=Cortex-M3" "--fpu=None" "-p" "C:\Program Files\CONFIG\debugger\ST\iostm32f10xxb.ddf" "--drv_verify_download" "--semihosting" "--device=STM32F10xxB" "-d" "jlink" "--drv_communication=USB0" "--jlink_speed=auto" "--jlink_initial_speed=32" "--jlink_reset_strategy=0,0" 

这个批处理文件运行的可执行文件叫做 cspybat。这个可执行文件的输出信息是:--backend 后面的所有参数都会传递给后端。

还要注意,有些参数是字符串,有些则不是。

解决方案

现在对我来说这个方法有效:

    """ MCU flashing function""" 
params = [r"C:\Program Files\bin\cspy",
          r"C:\Program Files\bin\arpro.dll",
          r"C:\Program Files\bin\arjink.dll",
          r"C:\Documents and Settings\USER\Desktop\Exe\GenerV530b.out",
          "--download_only", "--backend", "-B", "--endian=little", "--cpu=Cort3", "--fpu=None", "-p", 
          r"C:\Program Files\CONFIG\debugger\ST\iostm32f10xxb.ddf",
           "--drv_verify_download", "--semihosting", "--device=STM32F10xxB", "-d", "jlink", "--drv_communication=USB0",
            "--jlink_speed=auto", "--jlink_initial_speed=32", "--jlink_reset_strategy=0,0" ]
print(subprocess.list2cmdline(params))
p = subprocess.Popen(subprocess.list2cmdline(params))

2 个回答

0

首先,你并不需要那么多引号。所以把它们去掉。只有当参数中有文件名,并且这个文件名里有空格时,才需要加引号(真是让人无奈,Windows经常这样)。

你的参数其实就是一串字符串,其中有些需要加引号。因为Windows使用不标准的\作为路径分隔符,所以对于这些名字,使用“原始”字符串会更好。

params = [
    r'"C:\Program Files\Systems\Emb Work 5.4\arm\bin\mpr.dll"',
    r'"C:\Program Files\Systems\Emb Work 5.4\arm\bin\ajl.dll"',
    r'"C:\Documents and Settings\USER\Desktop\abc.out"',
    "--backend",
    "-B", 
    "--endian=little",
    "--cpu=Cortex",
    "--fpu=None",
    "-p",
    r'"C:\Program Files\unknown\abc.ddf"',
    "--drv_verify_download",
    "--semihosting",
    "--device=STM32F10xxB",
    "-d",
    "jjftk",
    "--drv_communication=USB0",
    "--speed=auto",
    "--initial_speed=32",
    "--reset_strategy=0,0"]

可以使用类似这样的方式:

program = r'"C:\Program Files\Systems\Emb Work 5.4\common\bin\run"'
subprocess.Popen( [program]+params )
28

在Windows中执行批处理文件的方法:

from subprocess import Popen
p = Popen("batchfile.bat", cwd=r"c:\directory\containing\batchfile")
stdout, stderr = p.communicate()

如果你不想执行批处理文件,而是想直接从Python中执行你问题里的命令,你需要对Popen的第一个参数进行一些尝试。

首先,第一个参数可以是一个字符串,也可以是一个序列。

所以你可以写:

p = Popen(r'"C:\Program Files\Systems\Emb Work 5.4\common\bin\run" "C:\Program Files\Systems\Emb Work 5.4\arm\bin\mpr.dll" ... ...', cwd=r"...")

或者

p = Popen([r"C:\Program Files\Systems\Emb Work 5.4\common\bin\run", r"C:\Program Files\Systems\Emb Work 5.4\arm\bin\mpr.dll", ...], cwd=r"...")
# ... notice how you don't need to quote the elements containing spaces

根据文档的说明:

在Windows上:Popen类使用CreateProcess()来执行子程序,它是基于字符串的。如果args是一个序列,它会通过list2cmdline()方法转换成字符串。请注意,并不是所有的Windows应用程序都以相同的方式解释命令行:list2cmdline()是为使用与MS C运行时相同规则的应用程序设计的。

所以如果你使用序列,它会被转换成字符串。我建议你先尝试使用序列,这样你就不需要为所有包含空格的元素加引号(list2cmdline()会帮你处理这个问题)。

如果遇到问题,我建议你把序列传给subprocess.list2cmdline(),然后检查输出结果。

编辑:

如果我是你,我会这样做:

a) 创建一个简单的Python脚本(testparams.py),内容如下:

import subprocess
params = [r"C:\Program Files\Systems\Emb Work 5.4\common\bin\run.exe", ...]
print subprocess.list2cmdline(params)

b) 从命令行运行这个脚本(python testparams.py),然后把输出复制粘贴到另一个命令行中,按回车看看会发生什么。

c) 如果不成功,就编辑Python文件,重复这个过程,直到成功为止。

撰写回答