Python 当多个参数包含空格时,如何使用子流程?

Python 当多个参数包含空格时,如何使用子流程?,python,subprocess,Python,Subprocess,我正在编写一个包装器脚本,它将运行一个vmware可执行文件,允许虚拟机启动/关闭/注册/注销操作的自动化。我试图使用subprocess来处理调用可执行文件的操作,但是subprocess没有正确处理可执行文件路径和参数中的空格。下面是一段代码片段: vmrun_cmd = r"c:/Program Files/VMware/VMware Server/vmware-cmd.bat" def vm_start(target_vm): list_arg = "start" lis

我正在编写一个包装器脚本,它将运行一个vmware可执行文件,允许虚拟机启动/关闭/注册/注销操作的自动化。我试图使用subprocess来处理调用可执行文件的操作,但是subprocess没有正确处理可执行文件路径和参数中的空格。下面是一段代码片段:

vmrun_cmd = r"c:/Program Files/VMware/VMware Server/vmware-cmd.bat"
def vm_start(target_vm):
    list_arg = "start"
    list_arg2 = "hard"
    if vm_list(target_vm):
            p = Popen([vmrun_cmd, target_vm, list_arg, list_arg2],   stdout=PIPE).communicate()[0]
            print p
    else:
            vm_register(target_vm)
            vm_start(target_vm)
def vm_list2(target_vm):
    list_arg = "-l"
    p = Popen([vmrun_cmd, list_arg], stdout=PIPE).communicate()[0]
    for line in p.split('\n'):
            print line
如果调用vm_list2函数,将得到以下输出:

$ ./vmware_control.py --list                                                
C:\Virtual Machines\QAW2K3Server\Windows Server 2003 Standard Edition.vmx
C:\Virtual Machines\ubunturouter\Ubuntu.vmx
C:\Virtual Machines\vacc\vacc.vmx
C:\Virtual Machines\EdgeAS-4.4.x\Other Linux 2.4.x kernel.vmx
C:\Virtual Machines\UbuntuServer1\Ubuntu.vmx
C:\Virtual Machines\Other Linux 2.4.x kernel\Other Linux 2.4.x kernel.vmx
C:\Virtual Machines\QAClient\Windows XP Professional.vmx
$ ./vmware_control.py --start "C:\Virtual Machines\ubunturouter\Ubuntu.vmx"
'c:\Program' is not recognized as an internal or external command,
operable program or batch file.
如果调用vm_start函数,它需要vm参数的路径,则会得到以下输出:

$ ./vmware_control.py --list                                                
C:\Virtual Machines\QAW2K3Server\Windows Server 2003 Standard Edition.vmx
C:\Virtual Machines\ubunturouter\Ubuntu.vmx
C:\Virtual Machines\vacc\vacc.vmx
C:\Virtual Machines\EdgeAS-4.4.x\Other Linux 2.4.x kernel.vmx
C:\Virtual Machines\UbuntuServer1\Ubuntu.vmx
C:\Virtual Machines\Other Linux 2.4.x kernel\Other Linux 2.4.x kernel.vmx
C:\Virtual Machines\QAClient\Windows XP Professional.vmx
$ ./vmware_control.py --start "C:\Virtual Machines\ubunturouter\Ubuntu.vmx"
'c:\Program' is not recognized as an internal or external command,
operable program or batch file.
显然,带有嵌入空格的第二个参数的存在正在改变子流程解释第一个参数的方式。对如何解决这个问题有什么建议吗

python2.5.2/cygwin/winxp

为什么要使用r“”?我相信,如果从开头删除“r”,它将被视为标准字符串,其中可能包含空格。然后,Python应该在将字符串发送到shell时正确引用该字符串

'c:\Program' is not recognized as an internal or external command,
operable program or batch file.
要获取此消息,您可以:

  • 使用
    shell=True

    vmrun_cmd = r"c:\Program Files\VMware\VMware Server\vmware-cmd.bat"
    subprocess.Popen(vmrun_cmd, shell=True)
    
  • 在代码的其他部分更改vmrun_cmd

  • 从vmware-cmd.bat内部获取此错误
  • 尝试的事项:

    • 打开python提示符,运行以下命令:

      subprocess.Popen([r"c:\Program Files\VMware\VMware Server\vmware-cmd.bat"])
      
    如果这样做有效,那么引用问题是不可能的。如果没有,您已经隔离了问题。

    2件事

    (一)

    您可能不想使用管道 如果子程序的输出大于64KB,则进程可能会崩溃。

    (二)
    Subprocess.Popen有一个关键字argument shell,使它看起来就像shell一直在解析您的参数一样,设置shell=True应该可以满足您的需要。

    我认为处理列表参数的list2cmdline(),会拆分空格上的任何字符串参数,除非该字符串包含双引号。所以我想

    vmrun_cmd = r'"c:/Program Files/VMware/VMware Server/vmware-cmd.bat"'
    
    成为你想要的人

    您可能还希望将其他参数(如
    target\u vm
    )用双引号括起来,假设它们也都表示要显示给命令行的不同参数。差不多

    r'"%s"' % target_vm
    
    (例如)应该适合


    D'A

    可能是愚蠢的建议,但不妨尝试以下方法,从等式中删除子流程+空格:

    import os
    from subprocess Popen, PIPE
    
    os.chdir(
        os.path.join("C:", "Program Files", "VMware", "VMware Server")
    )
    
    p = Popen(
        ["vmware-cmd.bat", target_vm, list_arg, list_arg2],
        stdout=PIPE
    ).communicate()[0]
    
    这可能也值得一试

    p = Popen(
        [os.path.join("C:", "Program Files", "VMware", "VMware Server", "vmware-cmd.bat"), ...
    

    在MS Windows上的Python中,subprocess.Popen类使用CreateProcess API启动进程。CreateProcess接受字符串,而不是类似于参数数组的内容。Python使用subprocess.list2cmdline将参数列表转换为CreateProcess的字符串

    如果我是你,我会看到subprocess.list2cmdline(args)返回什么(其中args是Popen的第一个参数)。看看它是否在第一个参数周围加上引号会很有趣

    当然,这种解释可能不适用于Cygwin环境


    说了这么多,我没有微软视窗。

    以下是我不喜欢的

    vmrun_cmd = r"c:/Program Files/VMware/VMware Server/vmware-cmd.bat"
    
    命令本身的名称中有空格,这让shell感到困惑。因此,“'c:\Program'不能识别为内部或外部命令, 可操作的程序或批处理文件。”

    选项1——将.BAT文件放在其他地方。实际上,请将您的所有VMWare放到其他地方。这里有一条规则:不要将“程序文件”目录用于任何内容。这是错误的

    选项2——引用
    vmrun\u cmd

    vmrun_cmd = r'"c:/Program Files/VMware/VMware Server/vmware-cmd.bat"'
    

    如果路径中有空格,我发现最简单的方法是正确解释它们

    subprocess.call('""' + path + '""')
    

    我不知道为什么它需要双引号,但这就是它的工作原理。

    一个问题是,如果命令被引号包围并且没有空格,这也可能会混淆shell

    'c:\Program' is not recognized as an internal or external command,
    operable program or batch file.
    
    所以我这样做:

    if ' ' in raw_cmd:
        fmt = '"%s"'
    else:
        fmt = '%s'
    
    cmd = fmt % raw_cmd
    

    在过去的三年中,这是一个相当困难的问题……到目前为止,没有任何声明起作用,也没有使用r“”或带有列表的Popen等等。最终的效果是格式字符串和r“”的组合。所以我的解决方案是:

    subprocess.Popen("{0} -f {1}".format(pathToExe, r'"%s"' % pathToVideoFileOrDir))
    

    其中,变量pathToExe和PathToVideoFileOrder的路径中都有空格。在格式化字符串中使用\“不起作用,并导致相同的错误,即无法再正确检测到第一条路径。

    我已对此进行了检查,原始字符串状态不会改变行为。使用r“…\”是有意义的。。。“用于Windows文件名。为什么c:/Program Files/VMware/VMware Server/VMware-cmd.bat中的斜杠走错了方向?是不是c:\ProgramFiles\?好吧,cygwin是*nix端口,所以它似乎喜欢标准的*nix斜线符号(或者我理解为标准的)*nix斜线符号。我的理解是,子流程应该将分隔符转换为基础系统所需的任何内容。现在解决了吗?顺序:1:我明确避免设置shell=True,所以不是这样。2:vmrun_cmd是一个全局常量,每次使用的方式完全相同。3:没有。在发生错误时,可执行文件还没有被调用-这是指定其路径的字符串的开始。好吧,target_vm是一个参数,而不是一个常量,那么在这种情况下双引号的最佳方法是什么呢?我可能会将其表述为r'%s''%target_vmMan,这在注释中很难理解。将其移至答案。list2cmdline文档链接已断开,并且该内容仅在上进行了记录——请注意,这是2.6版的文档,此后已从中删除