El módulo de subproceso de Python arroja resultados diferentes desde el shell de Unix

Estoy tratando de obtener una lista de los archivos CSV en un directorio con python. Esto es realmente fácil dentro de Unix:

ls -l *.csv 

Y, como era de esperar, obtengo una lista de los archivos que terminan con .csv en mi directorio. Sin embargo, cuando bash el equivalente de Python usando el módulo de Subproceso:

 >>> import subprocess as sp >>> sp.Popen(["ls", "-l", "*.csv"], stdout = sp.PIPE)  >>> ls: cannot access *.csv: No such file or directory 

¿Alguien puede explicar qué está pasando?

Edición : Agregar shell = True elimina el error, pero en lugar de obtener una lista de solo archivos CSV, obtengo una lista de todos los archivos del directorio.

Si desea que se comporte como lo hace en el shell, debe pasar shell=True (su millaje puede variar aquí, dependiendo de su sistema y shell). En su caso, el problema es que cuando hace ls -l *.csv , el shell evalúa lo que * significa, no ls . ( ls es simplemente el formato de sus resultados, pero el shell ha hecho el trabajo pesado para determinar qué archivos coinciden con *.csv ). Subprocess hace que ls trate *.csv literalmente, y busque un archivo con ese nombre específico, que por supuesto no hay ninguno (ya que es un nombre de archivo bastante difícil de crear).

Lo que realmente debería estar haciendo es usar os.listdir y filtrar los nombres usted mismo.

¿Por qué no usar glob en su lugar? ¡Va a ser más rápido que “desbaratar”!

 import glob glob.glob('*.csv') 

Esto le da solo los nombres, no toda la información adicional que ls -l supplies, aunque puede obtener información adicional con os.stat llamadas os.stat en archivos de interés.

Si realmente debes usar ls -l , creo que quieres pasarlo como una cadena para que el shell realice la expansión en estrella necesaria:

 proc = sp.Popen('ls -l *.csv', shell=True, stdout=sp.PIPE) 

Cuando ingresa ls -l *.csv en el shell, el propio shell expande *.csv en una lista de todos los nombres de archivos que coinciden. Así que los argumentos para ls serán en realidad algo más como ls -l spam.txt eggs.txt ham.py

El comando ls no comprende los comodines en sí. Entonces, cuando le pasas el argumento *.csv , trata de tratarlo como un nombre de archivo y no hay ningún archivo con ese nombre. Como dice Nick, puede usar el parámetro shell=True para que Python invoque un shell para ejecutar el subproceso, y el shell expandirá los comodines por usted.

 p=subprocess.Popen(["ls", "-l", "*.out"], stdout = subprocess.PIPE, shell=True) 

causas

 /bin/sh -c ls -l *.out 

para ser ejecutado.

Si intenta este comando en un directorio, verá que, en la forma típica de mystifying-shell, se enumeran todos los archivos. Y la bandera -l se ignora. Esa es una pista.

Verás, la bandera -c está recogiendo solo los ls . El rest de los argumentos son devorados por /bin/sh , no por ls .

Para que este comando funcione correctamente en la terminal, debe escribir

 /bin/sh -c "ls -l *.out" 

Ahora / bin / sh ve el comando completo “ls -l * .out” como el argumento de la marca -c .

Así que para que esto funcione correctamente usando subprocess.Popen , lo mejor es pasar el comando como una sola cadena

 p=subprocess.Popen("ls -l *.out", stdout = subprocess.PIPE, shell=True) output,error=p.communicate() print(output)