Ejecutar comandos Bash en Python

299

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 bashCommandcomando 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?

mkn
fuente
2
Parece haber una diferencia en el entorno dependiendo de cómo se ejecute cwm. ¿Quizás tiene alguna configuración en su .bashrcque configura el entorno para el uso de bash interactivo?
Sven Marnach
¿Intentó ejecutar el comando desde la línea de comando cuando inició sesión en el servidor? Tu publicación solo dice que "la pegaste en la terminal".
Sven Marnach
@Sven: sí, quise decir que ejecuté el comando directamente en la terminal del servidor
mkn
Parece que hay una diferencia en PYTHONPATH dependiendo de cómo corras cwm. O tal vez hay una diferencia en PATH, y cwmse llaman diferentes versiones de . O diferentes versiones de Python. Es realmente difícil resolver esto sin acceso a la máquina ...
Sven Marnach

Respuestas:

314

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:

bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
import subprocess
process = subprocess.Popen(bashCommand.split(), stdout=subprocess.PIPE)
output, error = process.communicate()
usuario225312
fuente
8
Esto no hizo lo que quería cuando necesitaba hacer un cd 'path\to\somewhere'seguido de otro comando bash que necesitaba ejecutarse en ese lugar. @ user225312
AWrightIV
36
@AWrightIV Si necesita que su subproceso se ejecute en un directorio de trabajo en particular, puede usar el cwdargumento para Popen:subprocess.Popen(..., cwd='path\to\somewhere')
resistente al agua
77
Para mi comando necesitaba shell = True como aquí; stackoverflow.com/questions/18962785/…
user984003
44
Es mejor usar shlex.split () en lugar de string.split () en este caso
Alexey Sviridov
44
... ( stdout=fileredirige 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)
jfs
186

Para ampliar un poco las respuestas anteriores aquí, hay una serie de detalles que comúnmente se pasan por alto.

  • Prefiero subprocess.run()sobre subprocess.check_call()y amigos sobre subprocess.call()sobre subprocess.Popen()sobre os.system()sobre sobreos.popen()
  • Comprender y probablemente usar text=True, aka universal_newlines=True.
  • Comprenda el significado de shell=Trueo shell=Falsey cómo cambia las citas y la disponibilidad de las comodidades de shell.
  • Comprender las diferencias entre shy Bash
  • Comprenda cómo un subproceso está separado de su padre y, en general, no puede cambiar el padre.
  • Evite ejecutar el intérprete de Python como un subproceso de Python.

Estos 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 :

El enfoque recomendado para invocar subprocesos es utilizar la run()función para todos los casos de uso que pueda manejar. Para casos de uso más avanzados, la Popeninterfaz subyacente se puede usar directamente.

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 el subprocessmó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 un CompletedProcessobjeto 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 usar subprocess.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. El Popenobjeto 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 osdocumentación del módulo contiene la recomendación de preferir subprocesssobre os.system():

El subprocessmódulo proporciona instalaciones más potentes para generar nuevos procesos y recuperar sus resultados; usar ese módulo es preferible a usar esta función.

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.systemes problemático y cómo subprocessintenta resolver esos problemas.

os.popen()solía desanimarse aún más :

En desuso desde la versión 2.6: esta función está obsoleta. Usa el subprocessmódulo.

Sin embargo, desde algún momento en Python 3, se ha vuelto a implementar simplemente para usar subprocessy redirige a la subprocess.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 que os.system(). En el uso regular, generalmente debe verificar si el proceso finalizó con éxito, qué subprocess.check_call()y subprocess.check_output()qué hacer (donde este último también devuelve la salida estándar del subproceso terminado). De manera similar, generalmente debe usar check=Truecon a subprocess.run()menos que específicamente necesite permitir que el subproceso devuelva un estado de error.

En la práctica, con check=Trueo subprocess.check_*, Python lanzará una CalledProcessErrorexcepción si el subproceso devuelve un estado de salida distinto de cero.

Un error común subprocess.run()es omitir check=Truey sorprenderse cuando falla el código descendente si falla el subproceso.

Por otro lado, un problema común con check_call()y check_output()fue que los usuarios que usaron estas funciones a ciegas se sorprendieron cuando se produjo la excepción, por ejemplo, cuando grepno encontraron una coincidencia. (Probablemente debería reemplazar grepcon 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=Trueakauniversal_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 bytesbú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á bytescadenas en las cadenas stdouty stderr. 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.

normal = subprocess.run([external, arg],
    stdout=subprocess.PIPE, stderr=subprocess.PIPE,
    check=True,
    text=True)
print(normal.stdout)

convoluted = subprocess.run([external, arg],
    stdout=subprocess.PIPE, stderr=subprocess.PIPE,
    check=True)
# You have to know (or guess) the encoding
print(convoluted.stdout.decode('utf-8'))

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ñosa universal_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=Truey 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.

# XXX AVOID THIS BUG
buggy = subprocess.run('dig +short stackoverflow.com')

# XXX AVOID THIS BUG TOO
broken = subprocess.run(['dig', '+short', 'stackoverflow.com'],
    shell=True)

# XXX DEFINITELY AVOID THIS
pathological = subprocess.run(['dig +short stackoverflow.com'],
    shell=True)

correct = subprocess.run(['dig', '+short', 'stackoverflow.com'],
    # Probably don't forget these, too
    check=True, text=True)

# XXX Probably better avoid shell=True
# but this is nominally correct
fixed_but_fugly = subprocess.run('dig +short stackoverflow.com',
    shell=True,
    # Probably don't forget these, too
    check=True, text=True)

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 osed 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.

cmd = '''while read -r x;
   do ping -c 3 "$x" | grep 'round-trip min/avg/max'
   done <hosts.txt'''

# Trivial but horrible
results = subprocess.run(
    cmd, shell=True, universal_newlines=True, check=True)
print(results.stdout)

# Reimplement with shell=False
with open('hosts.txt') as hosts:
    for host in hosts:
        host = host.rstrip('\n')  # drop newline
        ping = subprocess.run(
             ['ping', '-c', '3', host],
             text=True,
             stdout=subprocess.PIPE,
             check=True)
        for line in ping.stdout.split('\n'):
             if 'round-trip min/avg/max' in line:
                 print('{}: {}'.format(host, line))

Algunas cosas a tener en cuenta aquí:

  • Con shell=False usted no necesita la cita que el shell requiere alrededor de las cadenas. Poner comillas de todos modos es probablemente un error.
  • A menudo tiene sentido ejecutar el menor código posible en un subproceso. Esto le da más control sobre la ejecución desde su código Python.
  • Dicho esto, las tuberías complejas de shell son tediosas y, a veces, difíciles de reimplementar en Python.

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.

  • La expansión de comodines, también conocida como Globbing, se puede reemplazar glob.glob()o muy a menudo con simples comparaciones de cadenas de Python como for 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, ~accountal directorio de inicio de otro usuario)
  • Las variables de shell como $SHELLo a $my_exported_varveces simplemente se pueden reemplazar con variables de Python. Las variables de shell exportados están disponibles como por ejemplo os.environ['SHELL'](el significado de exportes 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 La env=palabra clave El argumento de los subprocessmé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). Con shell=Falseusted deberá comprender cómo eliminar las comillas; por ejemplo, cd "$HOME"es equivalente a os.chdir(os.environ['HOME'])sin comillas alrededor del nombre del directorio. (Muy a menudocdde 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 ... )
  • La redirección le permite leer un archivo como su entrada estándar y escribir su salida estándar en un archivo. grep 'foo' <inputfile >outputfilese abre outputfilepara escribir y inputfileleer, y pasa su contenido como entrada estándar a grep, cuya salida estándar luego aterriza outputfile. Esto no suele ser difícil de reemplazar con código nativo de Python.
  • Las tuberías son una forma de redireccionamiento. echo foo | nlejecuta dos subprocesos, donde la salida estándar de echoes la entrada estándar de nl(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 el pipesmódulo en la biblioteca estándar de Python o un número de competidores de terceros más modernos y versátiles).
  • El control de trabajos le permite interrumpir trabajos, ejecutarlos en segundo plano, devolverlos al primer plano, etc. Las señales básicas de Unix para detener y continuar un proceso, por supuesto, también están disponibles en Python. Pero los trabajos son una abstracción de nivel superior en el shell que involucra grupos de procesos, etc., que debe comprender si desea hacer algo así desde Python.
  • Citar en el shell es potencialmente confuso hasta que comprenda que todo es básicamente una cadena. Por 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 shy Bash

subprocessejecuta sus comandos de shell a /bin/shmenos que solicite específicamente lo contrario (excepto, por supuesto, en Windows, donde utiliza el valor de la COMSPECvariable). 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).

subprocess.run('''
    # This for loop syntax is Bash only
    for((i=1;i<=$#;i++)); do
        # Arrays are Bash-only
        array[i]+=123
    done''',
    shell=True, check=True,
    executable='/bin/bash')

A subprocessestá separado de su padre y no puede cambiarlo

Un error algo común es hacer algo como

subprocess.run('foo=bar', shell=True)
subprocess.run('echo "$foo"', shell=True)  # Doesn't work

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;

subprocess.run('foo=bar; echo "$foo"', shell=True)

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

os.environ['foo'] = 'bar'

o pasar una configuración de entorno a un proceso secundario con

subprocess.run('echo "$foo"', shell=True, env={'foo': 'bar'})

(sin mencionar la obvia refactorización subprocess.run(['echo', 'bar']); pero echoes 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 importel 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 multiprocessingmódulo. También existe el threadingque 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 ).

tripleee
fuente
2
Para una exposición más detallada de cómo podría evitar llamar a Python como un subproceso, vea esta respuesta en una pregunta tangencialmente similar.
tripleee
44
Me sorprende que tuviera que publicar una nueva respuesta a una pregunta tan básica para mostrar cómo ejecutar el comando desde la pregunta de forma idiomática. Su respuesta es larga pero no veo ese ejemplo. Sin relación: evite el desecho de carga. Si check_call () funciona en su caso, úselo. Tuve que arreglar un código que se usaba a run()ciegas. La falta check=Trueprovocó 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.
jfs el
1
@jfs Gracias por los comentarios, de hecho estaba planeando agregar una sección sobre Bash vs shpero 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; +1
tripleee
¿ stderr/stdout = subprocess.PIPETiene una sobrecarga de rendimiento superior a la configuración predeterminada?
Stringers
1
@Stringers No lo he probado, pero no veo por qué debería hacerlo. Si conecta esas tuberías a algo que procesa algo, entonces, por supuesto, ese procesamiento debe ser aceptado; pero no sucede en la tubería en sí. El valor predeterminado es no capturar stdout o stderr en absoluto, es decir, lo que se imprima allí queda fuera de la visibilidad y el control de Python, al igual que con os.system().
tripleee
41

Llámalo con subproceso

import subprocess
subprocess.Popen("cwm --rdf test.rdf --ntriples > test.nt")

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

Jakob Bowyer
fuente
3
El swapmódulo obviamente está allí, porque ejecutar el comando desde el shell funciona.
Sven Marnach
2
No en el servidor, cuando lo ejecuta en el servidor hay un error de importación.
Jakob Bowyer
@mkn: "Entonces simplemente copié esa salida e hice una copia pegar en la terminal y presioné enter y funciona ..." - ¿Intentó esto en el servidor o en su máquina?
Sven Marnach
¿Está ejecutando esto en una computadora independiente bien pero no funciona cuando lo ejecuta en su servidor? O que son capaces de ejecutarlo en un terminal servidor pero no el propio servidor
Jakob Bowyer
1
es incorrecta Si no se utiliza 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 detalles
jfs
18

Es posible que use el programa bash, con el parámetro -c para ejecutar los comandos:

bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
output = subprocess.check_output(['bash','-c', bashCommand])
Maquinilla de afeitar
fuente
2
subprocess.check_output(bashCommand, shell=True)hace lo mismo Si su comando es una cadena estática, intente analizarlo en una lista y evite el shell=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)
tripleee
@tripleee nota: /bin/sh(usado por subproceso) no es necesariamente bash(no puede usar bashisms). Aunque uno podría usar executable='/bin/bashsi lo desea. Aquí hay un ejemplo de código
jfs el
2
es la primera respuesta donde el comando debe comenzar con éxito (las respuestas aceptadas y la segunda respuesta popular son simplemente incorrectas. Una pequeña objeción: check_output()aquí es inútil (la salida siempre está vacía debido a la > fileredirección; use check_call()en su lugar.
jfs
16

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

David Daniel
fuente
3
¡Bien hecho! Mucho más limpio y más intuitivo que el subproceso.
mjd2
Muchas gracias! ¡Estoy feliz de escucharlo!
David Daniel
2
Honestamente, esto debería adoptarse en la biblioteca estándar.
Joshua Detwiler
1
¿Hay alguna manera de capturar la salida del terminal usando Sultan?
alvas
Sí, puedes @alvas ... Aquí están los documentos sobre cómo hacerlo: sultan.readthedocs.io/en/latest/…
David Daniel
7

Según el error, falta un paquete denominado swap en el servidor. Esto lo /usr/bin/cwmrequiere. Si estás en Ubuntu / Debian, instálalo python-swapusando aptitude.

kichik
fuente
pero funciona cuando lo ejecuto directamente en la terminal ... así que el intercambio debe estar allí, ¿no?
mkn
Hay dos opciones. o no puede encontrarlo swapo no debería haberlo importado en primer lugar. puedes import swapmanualmente? ¿Funciona?
kichik
hm no puedo. Si comienzo a python escribiendo python en la terminal y luego escribo import swap, aparece el error "ImportError: ningún módulo llamado swap". Lo extraño es que funciona cuando ejecuto el comando cwm directamente en la terminal del servidor
mkn
Intente imprimir sys.pathdó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.
kichik
4

También puedes usar 'os.popen'. Ejemplo:

import os

command = os.popen('ls -al')
print(command.read())
print(command.close())

Salida:

total 16
drwxr-xr-x 2 root root 4096 ago 13 21:53 .
drwxr-xr-x 4 root root 4096 ago 13 01:50 ..
-rw-r--r-- 1 root root 1278 ago 13 21:12 bot.py
-rw-r--r-- 1 root root   77 ago 13 21:53 test.py

None
ricardo130
fuente
1
La documentación contiene un gran cuadro rojo: " Desaprobado desde la versión 2.6: esta función está obsoleta. Utilice el subprocessmódulo".
tripleee
1
Para ser justos, os.popenya no tiene esta advertencia, y ahora es simplemente una envoltura delgada subprocess.Popen().
tripleee
4

Para ejecutar el comando sin un shell, pase el comando como una lista e implemente la redirección en Python usando [subprocess]:

#!/usr/bin/env python
import subprocess

with open('test.nt', 'wb', 0) as file:
    subprocess.check_call("cwm --rdf test.rdf --ntriples".split(),
                          stdout=file)

Nota: no > test.ntal final. stdout=fileimplementa la redirección.


Para ejecutar el comando usando el shell en Python, pase el comando como una cadena y habilite shell=True:

#!/usr/bin/env python
import subprocess

subprocess.check_call("cwm --rdf test.rdf --ntriples > test.nt",
                      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 :

#!/usr/bin/env python
import subprocess

subprocess.check_call('program <(command) <(another-command)',
                      shell=True, executable='/bin/bash')
jfs
fuente
Quizás mencione que .split()no es adecuado cuando hay cadenas entre comillas, etc. Existe una rutina separada shlex.split()que hace frente a la sintaxis de shell arbitrariamente compleja.
tripleee
@tripleee las .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.
jfs
0

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:

import subprocess

args = ['echo', 'Hello!']
subprocess.Popen(args) // same as running `echo Hello!` on cmd line

args2 = ['echo', '-v', '"Hello Again"']
subprocess.Popen(args2) // same as running 'echo -v "Hello Again!"` on cmd line
Respuestas Respuestas
fuente
No, el último ejemplo es lo mismo que ejecutar echo -v '"Hello Again!"'con comillas simples alrededor de las comillas dobles.
tripleee
Además, para usarlo correctamente subprocesss.Popen, debe administrar el objeto de proceso resultante (como mínimo, realizar una wait()para evitar que se convierta en un proceso zombie).
tripleee