Las otras respuestas aquí explican adecuadamente las advertencias de seguridad que también se mencionan en la subprocess
documentación. Pero además de eso, la sobrecarga de iniciar un shell para iniciar el programa que desea ejecutar a menudo es innecesaria y definitivamente tonta para situaciones en las que realmente no utiliza ninguna de las funciones del shell. Además, la complejidad oculta adicional debería asustarlo, especialmente si no está muy familiarizado con el shell o los servicios que proporciona.
Cuando las interacciones con el shell no son triviales, ahora necesita que el lector y el responsable del script Python (que puede ser o no su futuro yo) comprenda tanto el script Python como el shell. Recuerde el lema de Python "explícito es mejor que implícito"; incluso cuando el código de Python va a ser algo más complejo que el script de shell equivalente (y a menudo muy breve), es mejor que elimine el shell y reemplace la funcionalidad con construcciones de Python nativas. Minimizar el trabajo realizado en un proceso externo y mantener el control dentro de su propio código en la medida de lo posible suele ser una buena idea simplemente porque mejora la visibilidad y reduce los riesgos de efectos secundarios deseados o no deseados.
La expansión de comodines, la interpolación variable y la redirección son fáciles de reemplazar con construcciones nativas de Python. Una tubería compleja de shell en la que partes o todas las partes no pueden reescribirse razonablemente en Python sería la única situación en la que quizás podría considerar usar el shell. Todavía debe asegurarse de comprender las implicaciones de rendimiento y seguridad.
En el caso trivial, para evitar shell=True
, simplemente reemplace
subprocess.Popen("command -with -options 'like this' and\\ an\\ argument", shell=True)
con
subprocess.Popen(['command', '-with','-options', 'like this', 'and an argument'])
Observe cómo el primer argumento es una lista de cadenas para pasar execvp()
, y cómo citar cadenas y metacaracteres de shell con barra invertida generalmente no es necesario (o útil o correcto). Quizás vea también ¿ Cuándo ajustar las comillas alrededor de una variable de shell?
Como comentario aparte, a menudo desea evitar Popen
si uno de los envoltorios más simples del subprocess
paquete hace lo que desea. Si tiene un Python lo suficientemente reciente, probablemente debería usarlo subprocess.run
.
- Con
check=True
esto fallará si el comando que ejecutó falló.
- Con
stdout=subprocess.PIPE
él capturará la salida del comando.
- De manera algo oscura, con
universal_newlines=True
él decodificará la salida en una cadena Unicode adecuada (de lo bytes
contrario, solo está en la codificación del sistema, en Python 3).
Si no, para muchas tareas, desea check_output
obtener la salida de un comando, mientras verifica que tuvo éxito, o check_call
si no hay salida para recopilar.
Terminaré con una cita de David Korn: "Es más fácil escribir un shell portátil que un script de shell portátil". Even subprocess.run('echo "$HOME"', shell=True)
no es portátil para Windows.
-l
se pasa a/bin/sh
(el shell) en lugar dells
programa en Unix sishell=True
. El argumento de cadena debe usarseshell=True
en la mayoría de los casos en lugar de una lista.