El subproceso de Python no interpreta “~” como se espera en cygwin

Sección 17.1.1.1. de los estados de documentación :

Si el shell es True, el comando especificado se ejecutará a través del shell. Esto puede ser útil si está utilizando Python principalmente para el flujo de control mejorado que ofrece en la mayoría de los shells del sistema y aún desea un acceso conveniente a otras funciones del shell, como shells, comodines de nombre de archivo, expansión de variables de entorno y expansión de ~ a la casa del usuario. directorio.

Sin embargo, en cygwin , la salida de bash no es la misma que la del subproceso de Python , a saber:

Golpetazo:

$ ls -ls ~rbarakx total 0 0 drwxr-xr-x 1 Administrator None 0 Aug 21 17:54 bash 0 drwxr-xr-x 1 Administrator None 0 Jul 11 09:11 python 

Pitón:

 >>> subprocess.call(["ls","-ls","~rbarakx"],shell=True) RCS mecha.py print_unicode.py req.py requests_results.html selen.py 0 

Parece que subprocess.call está ejecutando solo ls .

¿Puedes sugerir por qué?

Mi entorno:
Python : Python 2.7.3 (predeterminado, 18 de diciembre de 2012, 13:50:09) [GCC 4.5.3] en cygwin
cygwin : CYGWIN_NT-6.1-WOW64 … 1.7.22 (0.268 / 5/3) … i686 Cygwin
Windows : Windows 7 Ultimate

En Windows, el shell ( cmd.exe ) no admite la expansión de ~ al directorio principal de un usuario. Tampoco la expansión de comodines, para el caso. El documento de Python está muy orientado a UNIX / Linux en este punto.

“¡Pero espera!” Te oigo llorar “He instalado cygwin , tengo bash !” Claro que sí, pero no puedes esperar que Python lo sepa. Estás en Windows, así que va a utilizar el shell de Windows. Si no fuera así, los scripts de Python que esperaban que el shell sea cmd.exe se confundirían enormemente.

Parece que subprocess.call está ejecutando solo ls.

¿Puedes sugerir por qué?

Porque en Unix (cygwin) su llamada es equivalente a:

 # WRONG: DON'T DO IT rc = subprocess.call(["/bin/sh", "-c"] + ["ls","-ls","~rbarakx"]) 

En otras palabras, la lista de argumentos se pasa al shell mismo, es decir, ls no ve -ls o ~rbarakx se consideran argumentos /bin/sh .

Lo que quieres es:

 rc = subprocess.call(["/bin/sh", "-c"] + ["ls -ls ~rbarakx"]) 

O usando shell=True :

 rc = subprocess.call("ls -ls ~rbarakx", shell=True) 

Aquí está la cita de los documentos de subprocess para referencia :

En Unix con shell=True , el shell por defecto es /bin/sh . Si args es una cadena, la cadena especifica el comando que se ejecutará a través del shell. Esto significa que la cadena debe tener el formato exactamente como se escribiría en el indicador de comandos del shell. Esto incluye, por ejemplo, citar o hacer una barra diagonal inversa en los nombres de archivo con espacios en ellos. Si args es una secuencia, el primer elemento especifica la cadena de comando, y cualquier elemento adicional se tratará como argumentos adicionales al propio shell. Es decir, Popen hace el equivalente de:

 Popen(['/bin/sh', '-c', args[0], args[1], ...]) 

el énfasis es mio

También puedes ejecutar el comando sin ningún shell:

 import os import subprocess rc = subprocess.call(["ls", "-ls", os.path.expanduser("~rbarakx")]) 

Es interesante que al pasar el comando como una cadena en lugar de una tupla, parece funcionar como si lo estuvieras ejecutando en bash.

 subprocess.call(["ls -ls ~"], shell=True) 

Si insiste en usar la tupla, considere la siguiente solución:

 cmd = subprocess.list2cmdline(["ls","-ls","~rbarakx"]) subprocess.call(cmd, shell=True) 

o esto:

 from os.path import expanduser subprocess.call(["ls","-ls", expanduser("~rbarakx")])