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

时间:2009-04-30 01:17:04

标签: python subprocess

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

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

如果我调用vm_start函数,它需要一个path-to-vm参数,我得到以下输出:

$ ./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

10 个答案:

答案 0 :(得分:5)

'c:\Program' is not recognized as an internal or external command,
operable program or batch file.

要获得此消息,您可以:

  1. 使用shell=True

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

  3. 从vmware-cmd.bat
  4. 中获取此错误

    要尝试的事情:

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

      subprocess.Popen([r"c:\Program Files\VMware\VMware Server\vmware-cmd.bat"])
      

    如果可行,那么引用问题是不可能的。如果没有,你就已经解决了这个问题。

答案 1 :(得分:5)

如果路径中有空格,我发现最简单的解释方法就是这个。

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

我不知道为什么它需要双倍双引号,但这才有效。

答案 2 :(得分:2)

在MS Windows上的Python中,subprocess.Popen类使用CreateProcess API来启动该过程。 CreateProcess接受一个字符串,而不是像一个参数数组。 Python使用subprocess.list2cmdline将args列表转换为CreateProcess的字符串。

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

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

说完这一切之后,我没有MS Windows。

答案 3 :(得分:1)

我相信list2cmdline()正在处理你的列表args,除非字符串包含双引号,否则会在空格上拆分任何字符串arg。所以我希望

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

成为你想要的。

你也可能希望用双引号括起其他参数(如target_vm),假设它们也代表一个独特的arg呈现给命令行。像

这样的东西
r'"%s"' % target_vm

(例如)应该适合。

请参阅the list2cmdline documentation

D'A

答案 4 :(得分:0)

一个问题是,如果命令被引号括起来并且没有空格,那么这也可能会混淆shell。

所以我这样做:

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

cmd = fmt % raw_cmd

答案 5 :(得分:0)

对于我们的最后三个来说,这是一个相当困难的问题....到目前为止没有任何说明有效,也没有使用r“”或Popen列表等等。到底是什么工作是格式字符串和r“”的组合。所以我的解决方案是:

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

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

答案 6 :(得分:-1)

2件事

1)

您可能不想使用Pipe 如果子程序的输出大于64KB,则您的进程可能会崩溃。 http://thraxil.org/users/anders/posts/2008/03/13/Subprocess-Hanging-PIPE-is-your-enemy/

2) Subprocess.Popen有一个关键字参数shell,就好像shell已经解析你的参数一样,设置shell = True应该做你想要的。

答案 7 :(得分:-2)

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

答案 8 :(得分:-2)

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

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"), ...

答案 9 :(得分:-2)

这是我不喜欢的

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

你在命令本身的名字中有空格 - 这让你的shell感到困惑。因此,''c:\ Program'不被识别为内部或外部命令, 可操作的程序或批处理文件。“

选项1 - 将.BAT文件放在其他位置。实际上,将所有VMWare放在其他地方。以下是规则:不要使用“程序文件”目录。这是错误的。

选项2 - 引用vmrun_cmd

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