Estoy tratando de entender cuál es la motivación detrás del uso de las funciones de biblioteca de Python para ejecutar tareas específicas del sistema operativo, como crear archivos / directorios, cambiar los atributos de los archivos, etc., en lugar de simplemente ejecutar esos comandos a través de os.system()
o subprocess.call()
.
Por ejemplo, ¿por qué querría usar en os.chmod
lugar de hacerlo os.system("chmod...")
?
Entiendo que es más "pitónico" usar los métodos de biblioteca disponibles de Python tanto como sea posible en lugar de simplemente ejecutar comandos de shell directamente. Pero, ¿hay alguna otra motivación detrás de hacer esto desde un punto de vista funcional?
Solo estoy hablando de ejecutar comandos simples de shell de una línea aquí. Cuando necesitamos más control sobre la ejecución de la tarea, entiendo que usar el subprocess
módulo tiene más sentido, por ejemplo.
fuente
print
cuando pudisteos.system("echo Hello world!")
?os.path
para manejar rutas en lugar de manejarlas manualmente: funciona en todos los sistemas operativos donde se ejecuta.os.chmod
no va a llamar alchmod
programa como lo haría el shell. El uso deos.system('chmod ...')
lanzamientos de una cáscara de interpretar una cadena para llamar a otro ejecutable para hacer una llamada a la Cchmod
función, mientras queos.chmod(...)
va mucho más directamente a la carpeta Cchmod
.Respuestas:
Es más rápido ,
os.system
ysubprocess.call
crear nuevos procesos que es innecesario para algo tan simple. De hecho,os.system
ysubprocess.call
con elshell
argumento usualmente se crean al menos dos procesos nuevos: el primero es el shell y el segundo el comando que estás ejecutando (si no es un shell incorporadotest
).Algunos comandos son inútiles en un proceso separado . Por ejemplo, si ejecuta
os.spawn("cd dir/")
, cambiará el directorio de trabajo actual del proceso secundario, pero no del proceso de Python. Necesitas usaros.chdir
para eso.No tiene que preocuparse por los caracteres especiales interpretados por el shell.
os.chmod(path, mode)
funcionará sin importar cuál sea el nombre de archivo, mientrasos.spawn("chmod 777 " + path)
que fallará horriblemente si el nombre de archivo es similar; rm -rf ~
. (Tenga en cuenta que puede solucionar esto si lo usasubprocess.call
sin elshell
argumento).No tiene que preocuparse por los nombres de archivo que comienzan con un guión .
os.chmod("--quiet", mode)
cambiará los permisos del archivo nombrado--quiet
, peroos.spawn("chmod 777 --quiet")
fallará, como--quiet
se interpreta como un argumento. Esto es cierto incluso parasubprocess.call(["chmod", "777", "--quiet"])
.Tiene menos preocupaciones entre plataformas y conchas cruzadas, ya que se supone que la biblioteca estándar de Python se ocupará de eso por usted. ¿Su sistema tiene
chmod
comando? ¿Está instalado? ¿Admite los parámetros que espera que admita? Elos
módulo intentará ser lo más multiplataforma posible y documentará cuando eso no sea posible.Si el comando que está ejecutando tiene un resultado que le interesa, debe analizarlo, lo que es más complicado de lo que parece, ya que puede olvidarse de las mayúsculas y minúsculas (nombres de archivos con espacios, pestañas y nuevas líneas), incluso cuando no me importa la portabilidad.
fuente
ls
odir
son bastante alto nivel para ciertos tipos de desarrolladores, al igual quebash
ocmd
oksh
o lo que sea shell son prefiere.ls
tiene un nivel más alto que eso, ya que no es una llamada directa a la API del kernel de su sistema operativo. Es una aplicación (pequeña).ls
odir
peroopendir()/readdir()
(api de linux) oFindFirstFile()/FindNextFile()
(api de windows) oFile.listFiles
(API de Java) oDirectory.GetFiles()
(C #). Todos estos están estrechamente vinculados a una llamada directa al sistema operativo. Algunos pueden ser tan simples como insertar un número en un registro y llamarint 13h
para activar el modo kernel.Es mas seguro. Para darle una idea aquí hay un script de ejemplo
Si la entrada del usuario fuera
test; rm -rf ~
así, se eliminaría el directorio de inicio.Es por eso que es más seguro usar la función integrada.
Por lo tanto, también debería utilizar el subproceso en lugar del sistema.
fuente
Existen cuatro casos sólidos para preferir los métodos más específicos de Python en el
os
módulo en lugar de usaros.system
o elsubprocess
módulo al ejecutar un comando:os
módulo están disponibles en varias plataformas, mientras que muchos comandos de shell son específicos de os.os
módulo.Redundancia (ver código redundante ):
En realidad, está ejecutando un "intermediario" redundante en su camino a las llamadas eventuales del sistema (
chmod
en su ejemplo). Este intermediario es un nuevo proceso o sub-shell.De
os.system
:Y
subprocess
es solo un módulo para generar nuevos procesos.Puede hacer lo que necesita sin generar estos procesos.
Portabilidad (ver portabilidad del código fuente ):
El
os
objetivo del módulo es proporcionar servicios genéricos del sistema operativo y su descripción comienza con:Puede usar
os.listdir
tanto en Windows como en Unix. Intentar usaros.system
/subprocess
para esta funcionalidad te obligará a mantener dos llamadas (parals
/dir
) y verificar en qué sistema operativo estás. Esto no es tan portátil y va a causar frustración aún más adelante (véase Salida de gestión ).Comprender los resultados del comando:
Suponga que desea enumerar los archivos en un directorio.
Si está utilizando
os.system("ls")
/subprocess.call(['ls'])
, solo puede recuperar la salida del proceso, que es básicamente una cadena grande con los nombres de archivo.¿Cómo puede distinguir un archivo con un espacio en su nombre de dos archivos?
¿Qué sucede si no tiene permiso para enumerar los archivos?
¿Cómo debe asignar los datos a los objetos de Python?
Estos solo están fuera de mi cabeza, y si bien hay soluciones a estos problemas, ¿por qué resolver de nuevo un problema que se resolvió para usted?
Este es un ejemplo de la siguiente No te repitas principio (A menudo aduana mencionados como "seco") por no repetir una implementación que ya existe y está disponible gratuitamente para usted.
La seguridad:
os.system
ysubprocess
son poderosos Es bueno cuando necesitas este poder, pero es peligroso cuando no lo necesitas. Cuando lo usaos.listdir
, sabe que no puede hacer nada más que enumerar archivos o generar un error. Cuando usaos.system
osubprocess
para lograr el mismo comportamiento, puede terminar haciendo algo que no quiso hacer.Seguridad de inyección (ver ejemplos de inyección de conchas ) :
Si usa la entrada del usuario como un nuevo comando, básicamente le ha dado un shell. Esto es muy parecido a la inyección SQL que proporciona un shell en la base de datos para el usuario.
Un ejemplo sería un comando de la forma:
Esto puede explotarse fácilmente para ejecutar cualquier código arbitrario utilizando la entrada:
NASTY COMMAND;#
para crear el eventual:Hay muchos de estos comandos que pueden poner en riesgo su sistema.
fuente
Por una simple razón: cuando llama a una función de shell, crea un sub-shell que se destruye después de que su comando existe, por lo que si cambia el directorio en un shell, no afecta su entorno en Python.
Además, la creación de sub-shell lleva mucho tiempo, por lo que usar los comandos del sistema operativo directamente afectará su rendimiento
EDITAR
Tuve algunas pruebas de tiempo en ejecución:
La función interna se ejecuta más de 10 veces más rápido
EDIT2
Puede haber casos en los que invocar un ejecutable externo puede producir mejores resultados que los paquetes de Python: acabo de recordar un correo enviado por un colega mío que decía que el rendimiento de gzip llamado a través del subproceso fue mucho mayor que el rendimiento de un paquete de Python que utilizó. Pero ciertamente no cuando hablamos de paquetes estándar del sistema operativo que emulan comandos estándar del sistema operativo
fuente
%
uso del intérprete normal.time <path to script>
terminal y le dirá el tiempo real, el usuario y el proceso que tomó. Eso es si no tiene iPython y tiene acceso a la línea de comando de Unix.Las llamadas de shell son específicas del sistema operativo, mientras que las funciones del módulo de Python os no lo son, en la mayoría de los casos. Y evita generar un subproceso.
fuente
Es mucho más eficiente. El "shell" es solo otro binario del sistema operativo que contiene muchas llamadas al sistema. ¿Por qué incurrir en la sobrecarga de crear todo el proceso de shell solo para esa única llamada al sistema?
La situación es aún peor cuando se usa
os.system
para algo que no es un shell incorporado. Inicia un proceso de shell que a su vez inicia un ejecutable que luego (a dos procesos de distancia) realiza la llamada al sistema. Al menossubprocess
habría eliminado la necesidad de un proceso intermediario de shell.No es específico de Python, esto.
systemd
es una mejora para los tiempos de inicio de Linux por la misma razón: hace que el sistema necesario se llame a sí mismo en lugar de generar miles de shells.fuente