¿Cómo usar el subproceso cuando múltiples argumentos contienen espacios?

Estoy trabajando en un script de envoltura que ejercerá un ejecutable vmware, permitiendo la automatización de las acciones de inicio / apagado / registro / cancelación de registro de la máquina virtual. Estoy tratando de usar el subproceso para manejar la invocación del ejecutable, pero los espacios en la ruta de los ejecutables y en los parámetros del ejecutable no están siendo manejados correctamente por el subproceso. A continuación se muestra un fragmento de código:

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 

Si llamo a la función vm_list2, obtengo el siguiente resultado:

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

Si llamo a la función vm_start, que requiere un parámetro de ruta a vm, obtengo el siguiente resultado:

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

Aparentemente, la presencia de un segundo parámetro con espacios incrustados está alterando la forma en que el subproceso está interpretando el primer parámetro. ¿Algunas sugerencias para resolver esto?

python2.5.2 / cygwin / winxp

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

Para recibir este mensaje, usted es:

  1. Usando shell=True :

     vmrun_cmd = r"c:\Program Files\VMware\VMware Server\vmware-cmd.bat" subprocess.Popen(vmrun_cmd, shell=True) 
  2. Cambiando vmrun_cmd en otra parte de tu código

  3. Obteniendo este error de algo dentro de vmware-cmd.bat

Cosas para probar:

  • Abra un indicador de python, ejecute el siguiente comando:

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

Si eso funciona, entonces citar los problemas está fuera de cuestión. Si no, has aislado el problema.

Si tiene espacios en la ruta, la forma más fácil que he encontrado para interpretarlos correctamente es esta.

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

No sé por qué exactamente necesita comillas dobles, pero eso es lo que funciona.

En Python en MS Windows, la clase subprocess.Popen usa la API CreateProcess para iniciar el proceso. CreateProcess toma una cadena en lugar de algo como una matriz de argumentos. Python usa subprocess.list2cmdline para convertir la lista de argumentos en una cadena para CreateProcess.

Si yo fuera usted, vería lo que devuelve subprocess.list2cmdline (args) (donde args es el primer argumento de Popen). Sería interesante ver si está poniendo comillas alrededor del primer argumento.

Por supuesto, esta explicación podría no aplicarse en un entorno Cygwin.

Habiendo dicho todo esto, no tengo MS Windows.

Creo que list2cmdline (), que está procesando los argumentos de su lista, divide cualquier cadena arg en el espacio en blanco a menos que la cadena contenga comillas dobles. Así que me esperaba

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

ser lo que quieras

También es probable que desee rodear los otros argumentos (como target_vm ) entre comillas dobles en el supuesto de que ellos también representan un argumento distinto para presentar a la línea de comandos. Algo como

 r'"%s"' % target_vm 

(por ejemplo) debe adaptarse.

Ver la documentación de list2cmdline

D’A

Un problema es que si el comando está rodeado de comillas y no tiene espacios, también podría confundir al shell.

Así que hago esto:

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

Ese fue un problema bastante difícil para los últimos tres nuestros … nada de lo declarado hasta ahora funcionó, ni usar r “” o Popen con una lista y así sucesivamente. Lo que funcionó al final fue una combinación de cadena de formato yr “”. Así que mi solución es la siguiente:

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

donde ambas variables pathToExe y pathToVideoFileOrDir tienen espacios en blanco en su ruta. El uso de \ “dentro de la cadena con formato no funcionó y dio como resultado el mismo error de que la primera ruta ya no se detectó correctamente.

2 cosas

1)

Es probable que no quiera usar Pipe Si la salida del subprogtwig es mayor que 64KB, es probable que su proceso se bloquee. http://thraxil.org/users/anders/posts/2008/03/13/Subprocess-Hanging-PIPE-is-your-enemy/

2) Subprocess.Popen tiene un shell de argumento de palabra clave, lo que lo hace como si el shell hubiera estado analizando sus argumentos, estableciendo shell = True debería hacer lo que desea.

¿Por qué estás usando r “”? Creo que si elimina la “r” desde el principio, se tratará como una cadena estándar que puede contener espacios. Python debería entonces citar correctamente la cadena al enviarla al shell.

Posiblemente una sugerencia estúpida, pero tal vez intente lo siguiente, eliminar los subprocesos + espacios de la ecuación:

 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] 

También podría valer la pena intentarlo.

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

Esto es lo que no me gusta.

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

Tienes espacios en el nombre del comando en sí mismo, lo que está desconcertando a tu shell. Por lo tanto, “‘c: \ Programme’ no se reconoce como un comando interno o externo, un progtwig ejecutable o un archivo por lotes”.

Opción 1 – ponga su archivo .BAT en otro lugar. De hecho, ponga todos sus VMWare en otro lugar. Aquí está la regla: No use el directorio “Archivos de progtwig” para nada. Es simplemente incorrecto

Opción 2 – citar el valor vmrun_cmd

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