25

I'm working on a wrapper script that will exercise a vmware executable, allowing for the automation of virtual machine startup/shutdown/register/deregister actions. I'm trying to use subprocess to handle invoking the executable, but the spaces in the executables path and in parameters of the executable are not being handled correctly by subprocess. Below is a code fragment:

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

If I call the vm_list2 function, I get the following output:

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

If I call the vm_start function, which requires a path-to-vm parameter, I get the following output:

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

Apparently, the presence of a second parameter with embedded spaces is altering the way that subprocess is interpreting the first parameter. Any suggestions on how to resolve this?

python2.5.2/cygwin/winxp

5
  • Why are your slashes in c:/Program Files/VMware/VMware Server/vmware-cmd.bat going the wrong way? Isn't it c:\Program Files\... ?
    – S.Lott
    Commented Apr 30, 2009 at 1:28
  • 2
    Well, cygwin is *nix port, so it seems to like the standard (or what I understand to be the standard) *nix slash notation. My understanding is that subprocess should translate the separator to whatever the underlying system needs.
    – Rob Carr
    Commented Apr 30, 2009 at 1:37
  • The answers here are quite specific to Windows. On Unix-like platforms, the simplest solution by far is to (avoid shell=True and) pass the arguments as a list, like subprocess.run(["ffmpeg", "-o", "value for -o option", "file name with spaces.mp4"], check=True)
    – tripleee
    Commented Jul 10, 2022 at 7:42
  • @tripleee The question is specific to Windows, so that makes sense. Maybe the title should be edited to mention "in Windows"? Does VMWare even run in Unix?
    – TylerH
    Commented Jul 26, 2022 at 13:57
  • Sure it does; the topic isn't specific to VMware anyway.
    – tripleee
    Commented Jul 26, 2022 at 14:16

10 Answers 10

8

If you have spaces in the path, the easiest way I've found to get them interpreted properly is this.

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

I don't know why exactly it needs double double quotes, but that is what works.

5

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

4
  • Well, target_vm is an arguement, rather than a constant, so what's the best method for double quoting in that situation?
    – Rob Carr
    Commented Apr 30, 2009 at 2:30
  • I would probably phrase it as r'"%s"' % target_vm
    – darch
    Commented Apr 30, 2009 at 7:00
  • Man, that's hard to read in the comment. Moving it to the answer.
    – darch
    Commented Apr 30, 2009 at 7:01
  • The list2cmdline documentation link is broken and the thing is only barely documented on docs.python.org/2.6/library/subprocess.html -- note this is doc for version 2.6 and it has since been removed from docs.python.org/2.7/library/subprocess.html
    – Davide
    Commented Feb 29, 2016 at 23:22
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.

1
  • 1
    In order: 1: I explicitly stayed away from setting shell=True, so that isn't it. 2: vmrun_cmd is a global constant used in exactly the same way each time. 3: Nope. The executable hasn't been invoked by the time that error occurs - that's the start of the string specifying it's path.
    – Rob Carr
    Commented Apr 30, 2009 at 2:03
2

In Python on MS Windows, the subprocess.Popen class uses the CreateProcess API to started the process. CreateProcess takes a string rather than something like an array of arguments. Python uses subprocess.list2cmdline to convert the list of args to a string for CreateProcess.

If I were you, I'd see what subprocess.list2cmdline(args) returns (where args is the first argument of Popen). It would be interesting to see if it is putting quotes around the first argument.

Of course, this explanation might not apply in a Cygwin environment.

Having said all this, I don't have MS Windows.

1

One problem is that if the command is surrounded with quotes and doesn't have spaces, that could also confuse the shell.

So I do this:

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

cmd = fmt % raw_cmd
1
  • There are other characters which need quoting on Windows; IIRC & and ^ can exist inside filenames, too?
    – tripleee
    Commented Jul 26, 2022 at 14:18
0

That was quite a hard problem for the last three hours... nothing stated so far did work, neither using r"" nor Popen with a list and so on. What did work in the end was a combination of format string and r"". So my solution is this:

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

where both variables pathToExe and pathToVideoFileOrDir have whitespaces in their path. Using " within the formatted string did not work and resulted in the same error that the first path is not detected any longer correctly.

1
  • This is rather obscure, and apparently combines two different techniques; but without access to your variables, we can only speculate. If pathToExe also contained spaces, you would need to either quote or escape them, too.
    – tripleee
    Commented Feb 14 at 16:57
-2

Possibly stupid suggestion, but perhaps try the following, to remove subprocess + spaces from the equation:

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]

It might also be worth trying..

p = Popen(
    [os.path.join("C:", "Program Files", "VMware", "VMware Server", "vmware-cmd.bat"), ...
-2
  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.

-3

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.

2
  • I've checked this, and the raw string status doesn't change the behavior.
    – Rob Carr
    Commented Apr 30, 2009 at 1:24
  • 2
    Makes sense to use r"...\..." for Windows file names.
    – S.Lott
    Commented Apr 30, 2009 at 1:27
-3

Here's what I don't like

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

You've got spaces in the name of the command itself -- which is baffling your shell. Hence the "'c:\Program' is not recognized as an internal or external command, operable program or batch file."

Option 1 -- put your .BAT file somewhere else. Indeed, put all your VMWare somewhere else. Here's the rule: Do Not Use "Program Files" Directory For Anything. It's just wrong.

Option 2 -- quote the vmrun_cmd value

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