23

我正在开发一个包装脚本,它将运行一个 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

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

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

4

10 回答 10

8

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

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

我不知道为什么它需要双双引号,但这就是有效的。

于 2009-04-30T13:41:10.677 回答
4
'c:\Program' is not recognized as an internal or external command,
operable program or batch file.

To get this message, you are either:

  1. Using shell=True:

    vmrun_cmd = r"c:\Program Files\VMware\VMware Server\vmware-cmd.bat"
    subprocess.Popen(vmrun_cmd, shell=True)
    
  2. Changing vmrun_cmd on other part of your code

  3. Getting this error from something inside vmware-cmd.bat

Things to try:

  • Open a python prompt, run the following command:

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

If that works, then quoting issues are out of the question. If not, you've isolated the problem.

于 2009-04-30T01:46:59.037 回答
3

I believe that list2cmdline(), which is doing the processing of your list args, splits any string arg on whitespace unless the string contains double quotes. So I would expect

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

to be what you want.

You'll also likely want to surround the other arguments (like target_vm) in double quotes on the assumption that they, too, each represent a distinct arg to present to the command line. Something like

r'"%s"' % target_vm

(for example) should suit.

See the list2cmdline documentation

D'A

于 2009-04-30T02:00:13.203 回答
2

在 MS Windows 上的 Python 中, subprocess.Popen 类使用 CreateProcess API 来启动进程。CreateProcess 采用字符串而不是参数数组之类的东西。Python 使用 subprocess.list2cmdline 将 args 列表转换为 CreateProcess 的字符串。

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

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

说了这么多,我没有MS Windows。

于 2009-04-30T04:11:00.313 回答
1

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

所以我这样做:

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

cmd = fmt % raw_cmd
于 2009-06-14T15:09:33.527 回答
0

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

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

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

于 2016-04-08T14:48:01.507 回答
-1

2 things

1)

You probably don't want to use Pipe If the output of the subprogram is greater than 64KB it is likely your process will crash. http://thraxil.org/users/anders/posts/2008/03/13/Subprocess-Hanging-PIPE-is-your-enemy/

2) Subprocess.Popen has a keyword argument shell, making it as if the shell has been parsing your arguments, setting shell=True should do what you want.

于 2009-04-30T01:49:23.947 回答
-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"'
于 2009-04-30T13:19:59.277 回答
-2

Why are you using r""? I believe that if you remove the "r" from the beginning, it will be treated as a standard string which may contain spaces. Python should then properly quote the string when sending it to the shell.

于 2009-04-30T01:22:40.157 回答
-2

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

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"), ...
于 2009-04-30T03:17:30.817 回答