Ejecutar comando de shell y capturar la salida

909

Quiero escribir una función que ejecute un comando de shell y devuelva su salida como una cadena , sin importar si es un mensaje de error o de éxito. Solo quiero obtener el mismo resultado que habría obtenido con la línea de comando.

¿Cuál sería un ejemplo de código que haría tal cosa?

Por ejemplo:

def run_command(cmd):
    # ??????

print run_command('mysqladmin create test -uroot -pmysqladmin12')
# Should output something like:
# mysqladmin: CREATE DATABASE failed; error: 'Can't create database 'test'; database exists'
Luz plateada
fuente

Respuestas:

1141

La respuesta a esta pregunta depende de la versión de Python que esté utilizando. El enfoque más simple es usar la subprocess.check_outputfunción:

>>> subprocess.check_output(['ls', '-l'])
b'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

check_outputejecuta un solo programa que toma solo argumentos como entrada. 1 Devuelve el resultado exactamente como se imprimió stdout. Si necesita escribir una entrada stdin, salte a las secciones runo Popen. Si desea ejecutar comandos de shell complejos, vea la nota shell=Trueal final de esta respuesta.

La check_outputfunción funciona en casi todas las versiones de Python que todavía se usan ampliamente (2.7+). 2 Pero para versiones más recientes, ya no es el enfoque recomendado.

Versiones modernas de Python (3.5 o superior): run

Si está utilizando Python 3.5 o superior, y no necesita compatibilidad con versiones anteriores , se recomienda la nueva runfunción . Proporciona una API muy general de alto nivel para el subprocessmódulo. Para capturar la salida de un programa, pase la subprocess.PIPEbandera al stdoutargumento de la palabra clave. Luego acceda al stdoutatributo del CompletedProcessobjeto devuelto :

>>> import subprocess
>>> result = subprocess.run(['ls', '-l'], stdout=subprocess.PIPE)
>>> result.stdout
b'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

El valor de retorno es un bytesobjeto, por lo que si desea una cadena adecuada, la necesitará decode. Suponiendo que el proceso llamado devuelve una cadena codificada en UTF-8:

>>> result.stdout.decode('utf-8')
'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

Todo esto se puede comprimir en una sola línea:

>>> subprocess.run(['ls', '-l'], stdout=subprocess.PIPE).stdout.decode('utf-8')
'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

Si desea pasar la entrada al proceso stdin, pase un bytesobjeto al inputargumento de la palabra clave:

>>> cmd = ['awk', 'length($0) > 5']
>>> input = 'foo\nfoofoo\n'.encode('utf-8')
>>> result = subprocess.run(cmd, stdout=subprocess.PIPE, input=input)
>>> result.stdout.decode('utf-8')
'foofoo\n'

Puede capturar errores pasando stderr=subprocess.PIPE(capturar a result.stderr) o stderr=subprocess.STDOUT(capturar result.stdoutjunto con la salida regular). Cuando la seguridad no es una preocupación, también puede ejecutar comandos de shell más complejos pasando shell=Truecomo se describe en las notas a continuación.

Esto agrega un poco de complejidad, en comparación con la antigua forma de hacer las cosas. Pero creo que vale la pena: ahora puede hacer casi cualquier cosa que necesite hacer solo con la runfunción.

Versiones anteriores de Python (2.7-3.4): check_output

Si está utilizando una versión anterior de Python, o necesita una modesta compatibilidad con versiones anteriores, probablemente pueda usar la check_outputfunción como se describe brevemente anteriormente. Ha estado disponible desde Python 2.7.

subprocess.check_output(*popenargs, **kwargs)  

Toma los mismos argumentos que Popen(ver más abajo) y devuelve una cadena que contiene la salida del programa. El comienzo de esta respuesta tiene un ejemplo de uso más detallado. En Python 3.5 y superior, check_outputes equivalente a ejecutar runcon check=Truey stdout=PIPE, y devolver solo el stdoutatributo.

Puede pasar stderr=subprocess.STDOUTa garantizar que los mensajes de error se incluyen en la salida devuelta - pero en algunas versiones de Python que pasa stderr=subprocess.PIPEa check_outputpuede causar bloqueos . Cuando la seguridad no es una preocupación, también puede ejecutar comandos de shell más complejos pasando shell=Truecomo se describe en las notas a continuación.

Si necesita canalizar stderro pasar la entrada al proceso, check_outputno estará a la altura de la tarea. Vea los Popenejemplos a continuación en ese caso.

Aplicaciones complejas y versiones heredadas de Python (2.6 y siguientes): Popen

Si necesita una profunda compatibilidad con versiones anteriores, o si necesita una funcionalidad más sofisticada que la que check_outputproporciona, tendrá que trabajar directamente con Popenobjetos, que encapsulan la API de bajo nivel para subprocesos.

El Popenconstructor acepta un solo comando sin argumentos, o una lista que contiene un comando como su primer elemento, seguido de cualquier número de argumentos, cada uno como un elemento separado en la lista. shlex.splitpuede ayudar a analizar cadenas en listas con el formato adecuado. PopenLos objetos también aceptan una gran cantidad de argumentos diferentes para la gestión de IO de procesos y la configuración de bajo nivel.

Para enviar entrada y capturar salida, communicatecasi siempre es el método preferido. Como en:

output = subprocess.Popen(["mycmd", "myarg"], 
                          stdout=subprocess.PIPE).communicate()[0]

O

>>> import subprocess
>>> p = subprocess.Popen(['ls', '-a'], stdout=subprocess.PIPE, 
...                                    stderr=subprocess.PIPE)
>>> out, err = p.communicate()
>>> print out
.
..
foo

Si configura stdin=PIPE, communicatetambién le permite pasar datos al proceso a través de stdin:

>>> cmd = ['awk', 'length($0) > 5']
>>> p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
...                           stderr=subprocess.PIPE,
...                           stdin=subprocess.PIPE)
>>> out, err = p.communicate('foo\nfoofoo\n')
>>> print out
foofoo

Nota respuesta de Aaron Hall , lo que indica que en algunos sistemas, es posible que necesite conjunto stdout, stderry stdinque todos PIPE(o DEVNULL) para llegar communicateal trabajo en absoluto.

En algunos casos raros, es posible que necesite una captura de salida compleja y en tiempo real. La respuesta de Vartec sugiere un camino a seguir, pero otros métodos que no communicateson propensos a puntos muertos si no se usan con cuidado.

Al igual que con todas las funciones anteriores, cuando la seguridad no es una preocupación, puede ejecutar comandos de shell más complejos al pasar shell=True.

Notas

1. Ejecutar comandos de shell: el shell=Trueargumento

Normalmente, cada llamada a run, check_outputo el Popenconstructor ejecuta un solo programa . Eso significa que no hay pipas elegantes de estilo bash. Si desea ejecutar comandos de shell complejos, puede pasar shell=True, que admiten las tres funciones.

Sin embargo, hacerlo plantea problemas de seguridad . Si está haciendo algo más que secuencias de comandos ligeras, es mejor que llame a cada proceso por separado y pase la salida de cada uno como entrada al siguiente, a través de

run(cmd, [stdout=etc...], input=other_output)

O

Popen(cmd, [stdout=etc...]).communicate(other_output)

La tentación de conectar directamente las tuberías es fuerte; resistelo. De lo contrario, es probable que veas puntos muertos o que tengas que hacer cosas extrañas como esta .

2. Consideraciones Unicode

check_outputdevuelve una cadena en Python 2, pero un bytesobjeto en Python 3. Vale la pena tomarse un momento para aprender sobre Unicode si aún no lo ha hecho.

senderle
fuente
55
Tanto con check_output()y communicate()tienes que esperar hasta que el proceso haya terminado, con poll()lo que obtienes la salida tal como viene. Realmente depende de lo que necesites.
vartec
2
No estoy seguro de si esto solo se aplica a versiones posteriores de Python, pero la variable outfue de tipo <class 'bytes'>para mí. Para obtener el resultado como una cadena, tuve que decodificarlo antes de imprimir así:out.decode("utf-8")
PolyMesh
1
@par ¿Eso no funciona para ti cuando pasas shell=True? Esto funciona para mi. No necesitas shlex.splitcuando pasas shell=True. shlex.splites para comandos que no son de shell. Creo que voy a sacar esa parte porque esto está enturbiando las aguas.
senderle
2
Python 3.5+ permite un argumento de palabra clave universal_newlines=Trueque le permite pasar y sacar cadenas Unicode en la codificación predeterminada del sistema. En 3.7 se cambió el nombre por el más sensato text=True.
tripleee
2
Para Python 3.6+ puede usar el encodingparámetro de en subprocess.runlugar de usar result.stdout.decode('utf-8'), puede usar subprocess.run(['ls', '-l'], stdout=subprocess.PIPE, encoding='utf-8').
Pierre
191

Esto es mucho más fácil, pero solo funciona en Unix (incluido Cygwin) y Python2.7.

import commands
print commands.getstatusoutput('wc -l file')

Devuelve una tupla con (return_value, output).

Para una solución que funcione tanto en Python2 como en Python3, use el subprocessmódulo en su lugar:

from subprocess import Popen, PIPE
output = Popen(["date"],stdout=PIPE)
response = output.communicate()
print response
byte_array
fuente
31
En desuso ahora, pero muy útil para versiones antiguas de python sin subproceso.check_output
static_rtti
22
Tenga en cuenta que esto es específico de Unix. Por ejemplo, fallará en Windows.
Zitrax
44
Tengo que trabajar en la versión antigua de Python 2.4 y esto fue MUY útil
javadba
1
¿Qué es PIPE? Vamos, muestra el código completo: subproceso
Kyle Bridenstine
@KyleBridenstine puedes editar respuestas.
Boris
106

Algo como eso:

def runProcess(exe):    
    p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    while(True):
        # returns None while subprocess is running
        retcode = p.poll() 
        line = p.stdout.readline()
        yield line
        if retcode is not None:
            break

Tenga en cuenta que estoy redirigiendo stderr a stdout, puede que no sea exactamente lo que desea, pero también quiero mensajes de error.

Esta función produce línea por línea a medida que avanzan (normalmente tendría que esperar a que finalice el subproceso para obtener la salida como un todo).

Para su caso, el uso sería:

for line in runProcess('mysqladmin create test -uroot -pmysqladmin12'.split()):
    print line,
vartec
fuente
Asegúrese de implementar algún tipo de bucle activo para obtener la salida para evitar el posible punto muerto waity las callfunciones.
André Caron
@Silver Light: su proceso probablemente esté esperando la entrada del usuario. Intente proporcionar un PIPEvalor stdiny cerrar ese archivo tan pronto como Popenregrese.
André Caron
44
-1: es un bucle infinito si retcodees 0. El cheque debería ser if retcode is not None. No se debe producir cadenas vacías (incluso una línea vacía es al menos un símbolo '\ n'): if line: yield line. Llama p.stdout.close()al final.
jfs
2
Probé el código con ls -l / dirname y se rompe después de enumerar dos archivos, mientras que hay muchos más archivos en el directorio
Vasilis
3
@fuenfundachtzig: .readlines()no volverá hasta que se lea toda la salida y, por lo tanto, se rompe para una salida grande que no cabe en la memoria. También para evitar la falta de datos almacenados en el búfer después de que salga el subproceso, debe haber un análogo deif retcode is not None: yield from p.stdout.readlines(); break
jfs
67

La respuesta de Vartec no lee todas las líneas, así que hice una versión que sí:

def run_command(command):
    p = subprocess.Popen(command,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.STDOUT)
    return iter(p.stdout.readline, b'')

El uso es el mismo que la respuesta aceptada:

command = 'mysqladmin create test -uroot -pmysqladmin12'.split()
for line in run_command(command):
    print(line)
Max Ekman
fuente
66
podría usar en return iter(p.stdout.readline, b'')lugar del bucle while
jfs
2
Es un uso genial de iter, ¡no lo sabía! Actualicé el código.
Max Ekman
Estoy bastante seguro de que stdout mantiene toda la salida, es un objeto continuo con un búfer. Utilizo una técnica muy similar para agotar toda la salida restante después de que se haya completado un Popen, y en mi caso, usar poll () y readline durante la ejecución para capturar la salida en vivo también.
Max Ekman el
He eliminado mi comentario engañoso. Puedo confirmar, p.stdout.readline()puede devolver la salida previamente vacía no almacenada, incluso si el proceso secundario ya ha salido ( p.poll()no lo está None).
jfs
Este código no funciona Vea aquí stackoverflow.com/questions/24340877/…
thang
61

Esta es una solución difícil pero súper simple que funciona en muchas situaciones:

import os
os.system('sample_cmd > tmp')
print open('tmp', 'r').read()

Se crea un archivo temporal (aquí es tmp) con la salida del comando y puede leer de él la salida deseada.

Nota adicional de los comentarios: puede eliminar el archivo tmp en el caso de un trabajo único. Si necesita hacer esto varias veces, no es necesario eliminar el tmp.

os.remove('tmp')
Mehdi Saman Booy
fuente
55
Hacky pero súper simple + funciona en cualquier lugar ... puedo combinarlo mktemppara que funcione en situaciones de rosca, supongo
Prakash Rajagaopal
2
Quizás sea el método más rápido, pero es mejor agregarlo os.remove('tmp')para que sea "sin archivos".
XuMuK
@XuMuK Tienes razón en el caso de un trabajo por única vez. Si es un trabajo repetitivo, tal vez no sea necesario eliminar
Mehdi Saman Booy
1
malo para la concurrencia, malo para las funciones reentrantes, malo para no dejar el sistema como estaba antes de que comenzara (sin limpieza)
2mia
1
@ 2mia ¡Obviamente es fácil por una razón! Si desea utilizar el archivo como un tipo de memoria compartida para lecturas y escrituras concurrentes, esta no es una buena opción. Pero, por s.th. como tener la salida de un comando (por ejemplo, ls o find o ...) puede ser una buena y rápida elección. Por cierto, si necesita una solución rápida para un problema simple, creo que es lo mejor. Si necesita una tubería, el subproceso funciona para usted de manera más eficiente.
Mehdi Saman Booy
44

Tuve el mismo problema pero descubrí una forma muy simple de hacer esto:

import subprocess
output = subprocess.getoutput("ls -l")
print(output)

Espero que ayude

Nota: Esta solución es específica de Python3 ya subprocess.getoutput()que no funciona en Python2

azhar22k
fuente
¿Cómo resuelve esto el problema del OP? Por favor elabora.
RamenChef
44
Devuelve la salida del comando como una cadena, tan simple como eso
azhar22k
1
Por supuesto, print es una declaración en Python 2. Debería poder darse cuenta de que esta es una respuesta de Python 3.
2
@Dev print (s) es válido para python 2. subprocess.getoutput no lo es.
user48956
2
Para la mayoría de los casos de uso, esto es lo que la gente probablemente querrá: fácil de recordar, no tiene que decodificar los resultados, etc. Gracias.
bwv549
18

Puede usar los siguientes comandos para ejecutar cualquier comando de shell. Los he usado en ubuntu.

import os
os.popen('your command here').read()

Nota: Esto está en desuso desde python 2.6. Ahora debes usar subprocess.Popen. Debajo está el ejemplo

import subprocess

p = subprocess.Popen("Your command", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
print p.split("\n")
Muhammad Hassan
fuente
2
En desuso desde la versión 2.6 - docs.python.org/2/library/os.html#os.popen
Filippo Vitale
1
@FilippoVitale Gracias. No sabía que está en desuso.
Muhammad Hassan
1
De acuerdo con raspberrypi.stackexchange.com/questions/71547/... os.popen() está en desuso en Python 2.6, pero está no en desuso en Python 3.x, ya que en 3.x se implementa utilizando subprocess.Popen().
JL
12

Su millaje puede variar, intenté el giro de @ senderle en la solución de Vartec en Windows en Python 2.6.5, pero estaba recibiendo errores, y ninguna otra solución funcionó. Mi error fue: WindowsError: [Error 6] The handle is invalid.

Descubrí que tenía que asignar PIPE a cada identificador para que devolviera el resultado que esperaba: lo siguiente funcionó para mí.

import subprocess

def run_command(cmd):
    """given shell command, returns communication tuple of stdout and stderr"""
    return subprocess.Popen(cmd, 
                            stdout=subprocess.PIPE, 
                            stderr=subprocess.PIPE, 
                            stdin=subprocess.PIPE).communicate()

y llame así, ( [0]obtiene el primer elemento de la tupla stdout):

run_command('tracert 11.1.0.1')[0]

Después de aprender más, creo que necesito estos argumentos de tubería porque estoy trabajando en un sistema personalizado que utiliza diferentes controladores, por lo que tuve que controlar directamente todos los estándares.

Para detener las ventanas emergentes de la consola (con Windows), haga esto:

def run_command(cmd):
    """given shell command, returns communication tuple of stdout and stderr"""
    # instantiate a startupinfo obj:
    startupinfo = subprocess.STARTUPINFO()
    # set the use show window flag, might make conditional on being in Windows:
    startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
    # pass as the startupinfo keyword argument:
    return subprocess.Popen(cmd,
                            stdout=subprocess.PIPE, 
                            stderr=subprocess.PIPE, 
                            stdin=subprocess.PIPE, 
                            startupinfo=startupinfo).communicate()

run_command('tracert 11.1.0.1')
Aaron Hall
fuente
1
Interesante: esto debe ser algo de Windows. Agregaré una nota que señala esto en caso de que las personas obtengan errores similares.
senderle
use en DEVNULLlugar de subprocess.PIPEsi no escribe / lee desde una tubería; de lo contrario, puede colgar el proceso secundario.
jfs
10

Tenía un sabor ligeramente diferente del mismo problema con los siguientes requisitos:

  1. Capture y devuelva mensajes STDOUT a medida que se acumulan en el búfer STDOUT (es decir, en tiempo real).
    • @vartec resolvió esto Pythonically con su uso de generadores y la
      palabra clave "rendimiento" arriba
  2. Imprima todas las líneas STDOUT ( incluso si el proceso sale antes de que el búfer STDOUT pueda leerse completamente )
  3. No desperdicie los ciclos de CPU sondeando el proceso a alta frecuencia
  4. Verifique el código de retorno del subproceso
  5. Imprima STDERR (separado de STDOUT) si obtenemos un código de retorno de error distinto de cero.

He combinado y ajustado las respuestas anteriores para llegar a lo siguiente:

import subprocess
from time import sleep

def run_command(command):
    p = subprocess.Popen(command,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE,
                         shell=True)
    # Read stdout from subprocess until the buffer is empty !
    for line in iter(p.stdout.readline, b''):
        if line: # Don't print blank lines
            yield line
    # This ensures the process has completed, AND sets the 'returncode' attr
    while p.poll() is None:                                                                                                                                        
        sleep(.1) #Don't waste CPU-cycles
    # Empty STDERR buffer
    err = p.stderr.read()
    if p.returncode != 0:
       # The run_command() function is responsible for logging STDERR 
       print("Error: " + str(err))

Este código se ejecutará igual que las respuestas anteriores:

for line in run_command(cmd):
    print(line)
El Aelfinn
fuente
1
¿Te importaría explicar cómo la adición de suspensión (.1) no desperdiciará los ciclos de la CPU?
Moataz Elmasry
2
Si continuamos llamando p.poll()sin dormir entre llamadas, desperdiciaríamos los ciclos de la CPU llamando a esta función millones de veces. En cambio, "aceleramos" nuestro ciclo diciéndole al sistema operativo que no necesitamos que nos molesten durante el próximo 1/10 de segundo, para que pueda llevar a cabo otras tareas. (Es posible que p.poll () también duerma, haciendo que nuestra declaración de suspensión sea redundante).
The Aelfinn
5

Dividir el comando inicial para el subprocesspodría ser complicado y engorroso.

Úselo shlex.split()para ayudarse.

Comando de muestra

git log -n 5 --since "5 years ago" --until "2 year ago"

El código

from subprocess import check_output
from shlex import split

res = check_output(split('git log -n 5 --since "5 years ago" --until "2 year ago"'))
print(res)
>>> b'commit 7696ab087a163e084d6870bb4e5e4d4198bdc61a\nAuthor: Artur Barseghyan...'

Sin shlex.split()el código se vería de la siguiente manera

res = check_output([
    'git', 
    'log', 
    '-n', 
    '5', 
    '--since', 
    '5 years ago', 
    '--until', 
    '2 year ago'
])
print(res)
>>> b'commit 7696ab087a163e084d6870bb4e5e4d4198bdc61a\nAuthor: Artur Barseghyan...'
Artur Barseghyan
fuente
1
shlex.split()es una conveniencia, especialmente si no sabe cómo funciona exactamente la cita en el shell; pero la conversión manual de esta cadena a la lista ['git', 'log', '-n', '5', '--since', '5 years ago', '--until', '2 year ago']no es nada difícil si comprende las citas.
tripleee
4

Si necesita ejecutar un comando de shell en varios archivos, esto me sirvió.

import os
import subprocess

# Define a function for running commands and capturing stdout line by line
# (Modified from Vartec's solution because it wasn't printing all lines)
def runProcess(exe):    
    p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    return iter(p.stdout.readline, b'')

# Get all filenames in working directory
for filename in os.listdir('./'):
    # This command will be run on each file
    cmd = 'nm ' + filename

    # Run the command and capture the output line by line.
    for line in runProcess(cmd.split()):
        # Eliminate leading and trailing whitespace
        line.strip()
        # Split the output 
        output = line.split()

        # Filter the output and print relevant lines
        if len(output) > 2:
            if ((output[2] == 'set_program_name')):
                print filename
                print line

Editar: Acabo de ver la solución de Max Persson con la sugerencia de JF Sebastian. Se adelantó e incorporó eso.

Ethan Strider
fuente
Popenacepta una cadena, pero luego la necesita shell=True, o una lista de argumentos, en cuyo caso debe pasar en ['nm', filename]lugar de una cadena. Lo último es preferible porque el shell agrega complejidad sin proporcionar ningún valor aquí. Pasar una cadena sin shell=Trueaparentemente funciona en Windows, pero eso podría cambiar en cualquier próxima versión de Python.
tripleee
2

Según @senderle, si usas python3.6 como yo:

def sh(cmd, input=""):
    rst = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, input=input.encode("utf-8"))
    assert rst.returncode == 0, rst.stderr.decode("utf-8")
    return rst.stdout.decode("utf-8")
sh("ls -a")

Actuará exactamente como si ejecutaras el comando en bash

Neo li
fuente
Estás reinventando los argumentos de las palabras clave check=True, universal_newlines=True. En otras palabras, subprocess.run()ya hace todo lo que hace su código.
tripleee
1

Si utiliza el subprocessmódulo python, puede manejar el STDOUT, STDERR y el código de comando de retorno por separado. Puede ver un ejemplo para la implementación completa del llamador de comandos. Por supuesto, puede ampliarlo try..exceptsi lo desea.

La siguiente función devuelve el código STDOUT, STDERR y Return para que pueda manejarlos en el otro script.

import subprocess

def command_caller(command=None)
    sp = subprocess.Popen(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=False)
    out, err = sp.communicate()
    if sp.returncode:
        print(
            "Return code: %(ret_code)s Error message: %(err_msg)s"
            % {"ret_code": sp.returncode, "err_msg": err}
            )
    return sp.returncode, out, err
milanbalazs
fuente
Otra pobre reimplementación de subprocess.run(). No reinventes la rueda.
tripleee
0

por ejemplo, execute ('ls -ahl') diferenciaba tres / cuatro posibles retornos y plataformas de SO:

  1. sin salida, pero se ejecuta con éxito
  2. salida de línea vacía, ejecutar con éxito
  3. ejecución fallida
  4. generar algo, ejecutar con éxito

funcionar a continuación

def execute(cmd, output=True, DEBUG_MODE=False):
"""Executes a bash command.
(cmd, output=True)
output: whether print shell output to screen, only affects screen display, does not affect returned values
return: ...regardless of output=True/False...
        returns shell output as a list with each elment is a line of string (whitespace stripped both sides) from output
        could be 
        [], ie, len()=0 --> no output;    
        [''] --> output empty line;     
        None --> error occured, see below

        if error ocurs, returns None (ie, is None), print out the error message to screen
"""
if not DEBUG_MODE:
    print "Command: " + cmd

    # https://stackoverflow.com/a/40139101/2292993
    def _execute_cmd(cmd):
        if os.name == 'nt' or platform.system() == 'Windows':
            # set stdin, out, err all to PIPE to get results (other than None) after run the Popen() instance
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
        else:
            # Use bash; the default is sh
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, executable="/bin/bash")

        # the Popen() instance starts running once instantiated (??)
        # additionally, communicate(), or poll() and wait process to terminate
        # communicate() accepts optional input as stdin to the pipe (requires setting stdin=subprocess.PIPE above), return out, err as tuple
        # if communicate(), the results are buffered in memory

        # Read stdout from subprocess until the buffer is empty !
        # if error occurs, the stdout is '', which means the below loop is essentially skipped
        # A prefix of 'b' or 'B' is ignored in Python 2; 
        # it indicates that the literal should become a bytes literal in Python 3 
        # (e.g. when code is automatically converted with 2to3).
        # return iter(p.stdout.readline, b'')
        for line in iter(p.stdout.readline, b''):
            # # Windows has \r\n, Unix has \n, Old mac has \r
            # if line not in ['','\n','\r','\r\n']: # Don't print blank lines
                yield line
        while p.poll() is None:                                                                                                                                        
            sleep(.1) #Don't waste CPU-cycles
        # Empty STDERR buffer
        err = p.stderr.read()
        if p.returncode != 0:
            # responsible for logging STDERR 
            print("Error: " + str(err))
            yield None

    out = []
    for line in _execute_cmd(cmd):
        # error did not occur earlier
        if line is not None:
            # trailing comma to avoid a newline (by print itself) being printed
            if output: print line,
            out.append(line.strip())
        else:
            # error occured earlier
            out = None
    return out
else:
    print "Simulation! The command is " + cmd
    print ""
Jerry T
fuente
0

El resultado se puede redirigir a un archivo de texto y luego volver a leerlo.

import subprocess
import os
import tempfile

def execute_to_file(command):
    """
    This function execute the command
    and pass its output to a tempfile then read it back
    It is usefull for process that deploy child process
    """
    temp_file = tempfile.NamedTemporaryFile(delete=False)
    temp_file.close()
    path = temp_file.name
    command = command + " > " + path
    proc = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    if proc.stderr:
        # if command failed return
        os.unlink(path)
        return
    with open(path, 'r') as f:
        data = f.read()
    os.unlink(path)
    return data

if __name__ == "__main__":
    path = "Somepath"
    command = 'ecls.exe /files ' + path
    print(execute(command))
SEÑOR
fuente
Claro que puede, pero ¿por qué quieres hacerlo? ¿y por qué usarías el shell en lugar de pasar stdout=temp_file?
tripleee
En realidad, en general tiene razón, pero en mi ejemplo ecls.exeparece que implementa otra herramienta de línea de comandos, por lo que la forma simple a veces no funcionó.
MR
0

acabo de escribir un pequeño script bash para hacer esto usando curl

https://gist.github.com/harish2704/bfb8abece94893c53ce344548ead8ba5

#!/usr/bin/env bash

# Usage: gdrive_dl.sh <url>

urlBase='https://drive.google.com'
fCookie=tmpcookies

curl="curl -L -b $fCookie -c $fCookie"
confirm(){
    $curl "$1" | grep jfk-button-action | sed -e 's/.*jfk-button-action" href="\(\S*\)".*/\1/' -e 's/\&amp;/\&/g'
}

$curl -O -J "${urlBase}$(confirm $1)"
harish2704
fuente