En mi máquina local, ejecuto un script de Python que contiene esta línea
bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
os.system(bashCommand)
Esto funciona bien
Luego ejecuto el mismo código en un servidor y recibo el siguiente mensaje de error
'import site' failed; use -v for traceback
Traceback (most recent call last):
File "/usr/bin/cwm", line 48, in <module>
from swap import diag
ImportError: No module named swap
Entonces, lo que hice fue insertar un print bashCommand
comando que me imprime que el comando en la terminal antes de ejecutarlo os.system()
.
Por supuesto, obtengo nuevamente el error (causado por os.system(bashCommand)
) pero antes de ese error imprime el comando en el terminal. Luego simplemente copié esa salida e hice una copia y pegué en la terminal y presioné enter y funciona ...
¿Alguien tiene idea de lo que está pasando?
cwm
. ¿Quizás tiene alguna configuración en su.bashrc
que configura el entorno para el uso de bash interactivo?cwm
. O tal vez hay una diferencia en PATH, ycwm
se llaman diferentes versiones de . O diferentes versiones de Python. Es realmente difícil resolver esto sin acceso a la máquina ...Respuestas:
No utilice
os.system
. Ha quedado en desuso a favor del subproceso . A partir de los documentos : "Este módulo tiene la intención de sustituir a varios mayores módulos y funciones:os.system
,os.spawn
".Como en tu caso:
fuente
cd 'path\to\somewhere'
seguido de otro comando bash que necesitaba ejecutarse en ese lugar. @ user225312cwd
argumento para Popen:subprocess.Popen(..., cwd='path\to\somewhere')
stdout=file
redirige la salida a un archivo en este caso. Se implementa> file
). Sería incorrecto pasar..., '>', 'file']
el último comando esperando la redirección (no funcionará sin un shell y si usa un shell, debe pasar el comando como una cadena)Para ampliar un poco las respuestas anteriores aquí, hay una serie de detalles que comúnmente se pasan por alto.
subprocess.run()
sobresubprocess.check_call()
y amigos sobresubprocess.call()
sobresubprocess.Popen()
sobreos.system()
sobre sobreos.popen()
text=True
, akauniversal_newlines=True
.shell=True
oshell=False
y cómo cambia las citas y la disponibilidad de las comodidades de shell.sh
y BashEstos temas se tratan con más detalle a continuación.
Prefiero
subprocess.run()
osubprocess.check_call()
La
subprocess.Popen()
función es un caballo de batalla de bajo nivel, pero es difícil de usar correctamente y terminas copiando / pegando varias líneas de código ... que convenientemente ya existen en la biblioteca estándar como un conjunto de funciones de envoltura de nivel superior para diversos fines, que se presentan con más detalle a continuación.Aquí hay un párrafo de la documentación :
Desafortunadamente, la disponibilidad de estas funciones de envoltura difiere entre las versiones de Python.
subprocess.run()
se introdujo oficialmente en Python 3.5. Está destinado a reemplazar todo lo siguiente.subprocess.check_output()
fue introducido en Python 2.7 / 3.1. Básicamente es equivalente asubprocess.run(..., check=True, stdout=subprocess.PIPE).stdout
subprocess.check_call()
fue introducido en Python 2.5. Básicamente es equivalente asubprocess.run(..., check=True)
subprocess.call()
fue introducido en Python 2.4 en elsubprocess
módulo original ( PEP-324 ). Básicamente es equivalente asubprocess.run(...).returncode
API de alto nivel vs
subprocess.Popen()
El refactorizado y extendido
subprocess.run()
es más lógico y más versátil que las funciones heredadas más antiguas que reemplaza. Devuelve unCompletedProcess
objeto que tiene varios métodos que le permiten recuperar el estado de salida, la salida estándar y algunos otros resultados e indicadores de estado del subproceso terminado.subprocess.run()
es el camino a seguir si simplemente necesita un programa para ejecutar y devolver el control a Python. Para escenarios más complicados (procesos en segundo plano, tal vez con E / S interactivas con el programa principal Python), aún necesita usarsubprocess.Popen()
y cuidar toda la tubería usted mismo. Esto requiere una comprensión bastante compleja de todas las partes móviles y no debe realizarse a la ligera. ElPopen
objeto más simple representa el proceso (posiblemente aún en ejecución) que necesita ser administrado desde su código por el resto de la vida útil del subproceso.Quizás debería enfatizarse que
subprocess.Popen()
simplemente crea un proceso. Si lo deja así, tiene un subproceso ejecutándose simultáneamente con Python, por lo que se trata de un proceso de "fondo". Si no necesita hacer entrada o salida o coordinarse con usted, puede hacer un trabajo útil en paralelo con su programa Python.Evitar
os.system()
yos.popen()
Desde el tiempo eterno (bueno, desde Python 2.5) la
os
documentación del módulo contiene la recomendación de preferirsubprocess
sobreos.system()
:El problema
system()
es que obviamente depende del sistema y no ofrece formas de interactuar con el subproceso. Simplemente se ejecuta, con salida estándar y error estándar fuera del alcance de Python. La única información que Python recibe es el estado de salida del comando (cero significa éxito, aunque el significado de los valores distintos de cero también depende en cierta medida del sistema).PEP-324 (que ya se mencionó anteriormente) contiene una justificación más detallada de por qué
os.system
es problemático y cómosubprocess
intenta resolver esos problemas.os.popen()
solía desanimarse aún más :Sin embargo, desde algún momento en Python 3, se ha vuelto a implementar simplemente para usar
subprocess
y redirige a lasubprocess.Popen()
documentación para obtener más detalles.Comprender y generalmente usar
check=True
También notará que
subprocess.call()
tiene muchas de las mismas limitaciones queos.system()
. En el uso regular, generalmente debe verificar si el proceso finalizó con éxito, quésubprocess.check_call()
ysubprocess.check_output()
qué hacer (donde este último también devuelve la salida estándar del subproceso terminado). De manera similar, generalmente debe usarcheck=True
con asubprocess.run()
menos que específicamente necesite permitir que el subproceso devuelva un estado de error.En la práctica, con
check=True
osubprocess.check_*
, Python lanzará unaCalledProcessError
excepción si el subproceso devuelve un estado de salida distinto de cero.Un error común
subprocess.run()
es omitircheck=True
y sorprenderse cuando falla el código descendente si falla el subproceso.Por otro lado, un problema común con
check_call()
ycheck_output()
fue que los usuarios que usaron estas funciones a ciegas se sorprendieron cuando se produjo la excepción, por ejemplo, cuandogrep
no encontraron una coincidencia. (Probablemente debería reemplazargrep
con código nativo de Python de todos modos, como se describe a continuación).Todo lo que cuenta, debe comprender cómo los comandos de shell devuelven un código de salida, y bajo qué condiciones devolverán un código de salida distinto de cero (error), y tomar una decisión consciente de cómo se debe manejar exactamente.
Comprender y probablemente usar
text=True
akauniversal_newlines=True
Desde Python 3, las cadenas internas de Python son cadenas Unicode. Pero no hay garantía de que un subproceso genere salida Unicode o cadenas en absoluto.
(Si las diferencias no son inmediatamente obvias, se recomienda la Unicode pragmática de Ned Batchelder , si no es absolutamente obligatoria, la lectura. Si lo prefiere, hay una presentación de video de 36 minutos detrás del enlace, aunque leer la página usted mismo probablemente tomará mucho menos tiempo. )
En el fondo, Python tiene que buscar un
bytes
búfer e interpretarlo de alguna manera. Si contiene un blob de datos binarios, no debe decodificarse en una cadena Unicode, porque ese es un comportamiento propenso a errores e inductor de errores, precisamente el tipo de comportamiento molesto que acribillaba muchos scripts de Python 2, antes de que hubiera una manera de distinguir adecuadamente entre texto codificado y datos binarios.Con
text=True
, le dices a Python que, de hecho, esperas datos textuales en la codificación predeterminada del sistema, y que deben decodificarse en una cadena Python (Unicode) de la mejor manera posible de Python (generalmente UTF-8 en cualquier moderadamente hasta sistema de fechas, excepto quizás Windows?)Si eso no es lo que solicita, Python solo le dará
bytes
cadenas en las cadenasstdout
ystderr
. Quizás en algún momento posterior lo hagas sabe que eran cadenas de texto después de todo, y conoce su codificación. Entonces, puedes decodificarlos.Python 3.7 introdujo el alias más corto, más descriptivo y comprensible
text
para el argumento de la palabra clave que anteriormente se llamaba de manera algo engañosauniversal_newlines
.Entender
shell=True
vsshell=False
Con
shell=True
usted pasa una sola cadena a su shell, y el shell lo toma desde allí.Con
shell=False
usted pasa una lista de argumentos al sistema operativo, sin pasar por el shell.Cuando no tiene un shell, guarda un proceso y se deshace de un cantidad bastante considerable de complejidad oculta, que puede o no albergar errores o incluso problemas de seguridad.
Por otro lado, cuando no tiene un shell, no tiene redirección, expansión de comodines, control de trabajos y una gran cantidad de otras características de shell.
Un error común es usar
shell=True
y luego pasarle a Python una lista de tokens, o viceversa. Esto sucede que funciona en algunos casos, pero está realmente mal definido y podría romperse de maneras interesantes.La respuesta común "pero funciona para mí" no es una refutación útil a menos que comprenda exactamente bajo qué circunstancias podría dejar de funcionar.
Ejemplo de refactorización
Muy a menudo, las características del shell se pueden reemplazar con código nativo de Python. Awk simple o
sed
scripts probablemente deberían simplemente traducirse a Python.Para ilustrar parcialmente esto, aquí hay un ejemplo típico pero un poco tonto que involucra muchas características de shell.
Algunas cosas a tener en cuenta aquí:
shell=False
usted no necesita la cita que el shell requiere alrededor de las cadenas. Poner comillas de todos modos es probablemente un error.El código refactorizado también ilustra cuánto hace realmente el shell para usted con una sintaxis muy concisa, para bien o para mal. Python dice que explícito es mejor que implícito, pero el código de Python es bastante detallado y podría parecer más complejo de lo que realmente es. Por otro lado, ofrece una serie de puntos en los que puede tomar el control en el medio de otra cosa, como lo ejemplifica trivialmente la mejora de que podemos incluir fácilmente el nombre del host junto con la salida del comando de shell. (Esto tampoco es un desafío en el shell, pero a expensas de otro desvío y quizás otro proceso).
Construcciones de Shell comunes
Para completar, aquí hay breves explicaciones de algunas de estas características de shell, y algunas notas sobre cómo tal vez se puedan reemplazar con las instalaciones nativas de Python.
glob.glob()
o muy a menudo con simples comparaciones de cadenas de Python comofor file in os.listdir('.'): if not file.endswith('.png'): continue
. Bash tiene varias otras instalaciones de expansión, como la.{png,jpg}
expansión de llaves y la expansión de{1..100}
tilde (se~
expande a su directorio de inicio y, en general,~account
al directorio de inicio de otro usuario)$SHELL
o a$my_exported_var
veces simplemente se pueden reemplazar con variables de Python. Las variables de shell exportados están disponibles como por ejemploos.environ['SHELL']
(el significado deexport
es hacer que la variable disponible para subprocesos -. Una variable que no está disponible para subprocesos obviamente no estar disponibles para Python se ejecuta como un subproceso de la concha, o viceversa Laenv=
palabra clave El argumento de lossubprocess
métodos le permite definir el entorno del subproceso como un diccionario, por lo que esa es una forma de hacer que una variable de Python sea visible para un subproceso). Conshell=False
usted deberá comprender cómo eliminar las comillas; por ejemplo,cd "$HOME"
es equivalente aos.chdir(os.environ['HOME'])
sin comillas alrededor del nombre del directorio. (Muy a menudocd
de todos modos no es útil ni necesario, y muchos principiantes omiten las comillas dobles alrededor de la variable y se salen con la suya hasta que un día ... )grep 'foo' <inputfile >outputfile
se abreoutputfile
para escribir yinputfile
leer, y pasa su contenido como entrada estándar agrep
, cuya salida estándar luego aterrizaoutputfile
. Esto no suele ser difícil de reemplazar con código nativo de Python.echo foo | nl
ejecuta dos subprocesos, donde la salida estándar deecho
es la entrada estándar denl
(en el nivel del sistema operativo, en sistemas tipo Unix, este es un identificador de archivo único). Si no puede reemplazar uno o ambos extremos de la tubería con código nativo de Python, quizás piense en usar un shell después de todo, especialmente si la tubería tiene más de dos o tres procesos (aunque mire elpipes
módulo en la biblioteca estándar de Python o un número de competidores de terceros más modernos y versátiles).ls -l /
lo tanto, es equivalente a,'ls' '-l' '/'
pero la cita alrededor de literales es completamente opcional. Las cadenas sin comillas que contienen metacaracteres de shell experimentan expansión de parámetros, tokenización de espacios en blanco y expansión de comodines; las comillas dobles evitan la tokenización de espacios en blanco y la expansión de comodines, pero permiten expansiones de parámetros (sustitución de variables, sustitución de comandos y procesamiento de barra invertida). Esto es simple en teoría, pero puede ser desconcertante, especialmente cuando hay varias capas de interpretación (un comando de shell remoto, por ejemplo).Comprender las diferencias entre
sh
y Bashsubprocess
ejecuta sus comandos de shell a/bin/sh
menos que solicite específicamente lo contrario (excepto, por supuesto, en Windows, donde utiliza el valor de laCOMSPEC
variable). Esto significa que varias funciones de solo Bash, como matrices,[[
etc. , no están disponibles.Si necesita usar la sintaxis de solo Bash, puede pasar la ruta al shell como
executable='/bin/bash'
(donde, por supuesto, si su Bash está instalado en otro lugar, debe ajustar la ruta).A
subprocess
está separado de su padre y no puede cambiarloUn error algo común es hacer algo como
que, aparte de la falta de elegancia, también revela una falta fundamental de comprensión de la parte "sub" del nombre "subproceso".
Un proceso secundario se ejecuta completamente separado de Python, y cuando finaliza, Python no tiene idea de lo que hizo (aparte de los vagos indicadores que puede inferir del estado de salida y salida del proceso secundario). Un niño generalmente no puede cambiar el entorno de los padres; no puede establecer una variable, cambiar el directorio de trabajo o, en pocas palabras, comunicarse con su padre sin la cooperación del padre.
La solución inmediata en este caso particular es ejecutar ambos comandos en un solo subproceso;
aunque obviamente este caso de uso en particular no requiere el shell en absoluto. Recuerde, puede manipular el entorno del proceso actual (y, por lo tanto, también sus hijos) a través de
o pasar una configuración de entorno a un proceso secundario con
(sin mencionar la obvia refactorización
subprocess.run(['echo', 'bar'])
; peroecho
es un mal ejemplo de algo que se ejecuta en un subproceso en primer lugar, por supuesto).No ejecutes Python desde Python
Este es un consejo ligeramente dudoso; Ciertamente, hay situaciones en las que tiene sentido o incluso es un requisito absoluto para ejecutar el intérprete de Python como un subproceso de un script de Python. Pero con mucha frecuencia, el enfoque correcto es simplemente
import
el otro módulo de Python en su script de llamada y llamar a sus funciones directamente.Si el otro script de Python está bajo su control y no es un módulo, considere convertirlo en uno . (Esta respuesta ya es demasiado larga, así que no profundizaré en los detalles aquí).
Si necesita paralelismo, puede ejecutar funciones de Python en subprocesos con el
multiprocessing
módulo. También existe elthreading
que ejecuta múltiples tareas en un solo proceso (que es más liviano y le brinda más control, pero también más restringido porque los hilos dentro de un proceso están estrechamente unidos y vinculados a un solo GIL ).fuente
run()
ciegas. La faltacheck=True
provocó un error que se evitaría si se utilizara check_call: "check" está en el nombre, no puede perderlo; es el valor predeterminado correcto: no ignore los errores en silencio. No leí más.sh
pero me ganaste . Estoy tratando de explicar los detalles con suficiente detalle para ayudar a los principiantes para quienes estas dificultades no son obvias, por lo que eso se vuelve un poco largo. El tuyo debería ser bastante suficiente de lo contrario; +1stderr/stdout = subprocess.PIPE
Tiene una sobrecarga de rendimiento superior a la configuración predeterminada?os.system()
.Llámalo con subproceso
El error que está recibiendo parece ser porque no hay un módulo de intercambio en el servidor, debe instalar el intercambio en el servidor y luego ejecutar el script nuevamente
fuente
swap
módulo obviamente está allí, porque ejecutar el comando desde el shell funciona.shell=True
, entonces debería usar una lista para pasar múltiples argumentos, es decir, utilizar['a', 'b', 'c']
en lugar de'a b c'
. Aunque una división ingenua no funcionará debido a> file
(redirección de shell) en el comando. Más detallesEs posible que use el programa bash, con el parámetro -c para ejecutar los comandos:
fuente
subprocess.check_output(bashCommand, shell=True)
hace lo mismo Si su comando es una cadena estática, intente analizarlo en una lista y evite elshell=True
; aunque en este caso necesita el shell para la redirección de todos modos, o de lo contrario necesitará refactorizarlo a Python puro -with open('test.nt', 'w') as dest: output = subprocess.check_output(['cwm' ,'--rdf', 'test.rdf', '--ntriples'], stdout=dest, shell=False)
/bin/sh
(usado por subproceso) no es necesariamentebash
(no puede usar bashisms). Aunque uno podría usarexecutable='/bin/bash
si lo desea. Aquí hay un ejemplo de códigocheck_output()
aquí es inútil (la salida siempre está vacía debido a la> file
redirección; usecheck_call()
en su lugar.Puedes usarlo
subprocess
, pero siempre sentí que no era una forma 'pitónica' de hacerlo. Así que creé Sultan (conector descarado) que facilita la ejecución de funciones de línea de comandos.https://github.com/aeroxis/sultan
fuente
Según el error, falta un paquete denominado swap en el servidor. Esto lo
/usr/bin/cwm
requiere. Si estás en Ubuntu / Debian, instálalopython-swap
usando aptitude.fuente
swap
o no debería haberlo importado en primer lugar. puedesimport swap
manualmente? ¿Funciona?sys.path
dónde funciona y dónde no. Luego intente buscar la carpeta de intercambio o swap.py en las carpetas impresas. Como dijo Sven, puede haber un problema con esos caminos, y esto lo ayudará a resolverlo.También puedes usar 'os.popen'. Ejemplo:
Salida:
fuente
subprocess
módulo".os.popen
ya no tiene esta advertencia, y ahora es simplemente una envoltura delgadasubprocess.Popen()
.Para ejecutar el comando sin un shell, pase el comando como una lista e implemente la redirección en Python usando
[subprocess]
:Nota: no
> test.nt
al final.stdout=file
implementa la redirección.Para ejecutar el comando usando el shell en Python, pase el comando como una cadena y habilite
shell=True
:Aquí el shell es responsable de la redirección de salida (
> test.nt
está en el comando).Para ejecutar un comando bash que use bashisms, especifique el ejecutable bash explícitamente, por ejemplo, para emular la sustitución del proceso bash :
fuente
.split()
no es adecuado cuando hay cadenas entre comillas, etc. Existe una rutina separadashlex.split()
que hace frente a la sintaxis de shell arbitrariamente compleja..split()
obras en este caso.shlex.split()
A veces puede ser útil, pero también puede fallar en algunos casos. Hay muchas cosas que podrían mencionarse. Puede comenzar con el enlace a la descripción de la etiqueta de subproceso proporcionada anteriormente.La forma pitónica de hacer esto es usar
subprocess.Popen
subprocess.Popen
toma una lista donde el primer elemento es el comando que se ejecutará seguido de cualquier argumento de línea de comando.Como ejemplo:
fuente
echo -v '"Hello Again!"'
con comillas simples alrededor de las comillas dobles.subprocesss.Popen
, debe administrar el objeto de proceso resultante (como mínimo, realizar unawait()
para evitar que se convierta en un proceso zombie).