Speichern Sie die Ausgabe des Unterprozesses. Öffnen Sie den Aufruf in einer Zeichenfolge

300

Ich versuche, einen Systemaufruf in Python durchzuführen und die Ausgabe in einer Zeichenfolge zu speichern, die ich im Python-Programm bearbeiten kann.

#!/usr/bin/python
import subprocess
p2 = subprocess.Popen("ntpq -p")

Ich habe ein paar Dinge ausprobiert, einschließlich einiger Vorschläge hier:

Abrufen der Ausgabe von subprocess.call ()

aber ohne Glück.

Kennzeichen
quelle
3
Es ist gut, immer den tatsächlichen Code, den Sie ausgeführt haben, und den tatsächlichen Traceback oder das unerwartete Verhalten für konkrete Fragen wie diese zu veröffentlichen. Ich weiß zum Beispiel nicht, was Sie versucht haben, um die Ausgabe zu erhalten, und ich vermute, dass Sie anfangs nicht so weit gekommen sind - Sie hätten einen Fehler erhalten, wenn Sie die Datei nicht gefunden hätten "ntpq -p", was ein anderer Teil von ist das Problem, als Sie fragen.
Mike Graham

Antworten:

467

In Python 2.7 oder Python 3

Anstatt ein PopenObjekt direkt zu erstellen, können Sie die subprocess.check_output()Funktion verwenden , um die Ausgabe eines Befehls in einer Zeichenfolge zu speichern:

from subprocess import check_output
out = check_output(["ntpq", "-p"])

In Python 2.4-2.6

Verwenden Sie die communicateMethode.

import subprocess
p = subprocess.Popen(["ntpq", "-p"], stdout=subprocess.PIPE)
out, err = p.communicate()

out ist was du willst.

Wichtiger Hinweis zu den anderen Antworten

Beachten Sie, wie ich den Befehl übergeben habe. Das "ntpq -p"Beispiel bringt eine andere Sache auf. Da Popendie Shell nicht aufgerufen wird, würden Sie eine Liste der Befehle und Optionen verwenden ["ntpq", "-p"].

Mike Graham
quelle
3
Wartet Python in diesem Fall auf den Abschluss dieses Systemaufrufs? Oder muss die Funktion wait / waitpid explizit aufgerufen werden?
NoneType
7
@NoneType, Popen.communicatewird erst zurückgegeben, wenn der Prozess beendet wurde.
Mike Graham
10
Wenn Sie einen Fehlerstrom erhalten möchten, fügen Sie stderr hinzu:p = subprocess.Popen(["ntpq", "-p"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Timofey
82
Vorsicht, subprocess.check_output()gibt ein bytesObjekt zurück, nicht ein str. Wenn Sie nur das Ergebnis drucken möchten, macht dies keinen Unterschied. Wenn Sie jedoch eine Methode wie myString.split("\n")das Ergebnis verwenden möchten , müssen Sie zuerst das bytesObjekt dekodieren : subprocess.check_output(myParams).decode("utf-8")zum Beispiel.
TanguyP
17
Das Hinzufügen universal_newlines=Trueals Parameter hat mir in Python 3 geholfen, das String-Objekt zu erhalten. Wenn universal_newlines True ist, werden sie im Textmodus mit Standardcodierung geöffnet. Andernfalls werden sie als binäre Streams geöffnet.
Jonathan Komar
38

Dies funktionierte bei mir für die Umleitung von stdout (stderr kann ähnlich behandelt werden):

from subprocess import Popen, PIPE
pipe = Popen(path, stdout=PIPE)
text = pipe.communicate()[0]

Wenn es bei Ihnen nicht funktioniert, geben Sie bitte genau das Problem an, das Sie haben.

Eli Bendersky
quelle
3
Dies erzeugt ein seltsames Objekt. Wenn ich es in einen String konvertiere, entweicht es Leerzeichen wie \n.
Tomáš Zato - Wiedereinsetzung Monica
1
Beachten Sie, dass dadurch nicht überprüft wird, ob der Unterprozess ordnungsgemäß ausgeführt wurde. Sie möchten das wahrscheinlich auch pipe.returncode == 0nach der letzten Zeile überprüfen .
Thakis
Der Grund dafür ist, dass Popenein Tupel von stdoutund zurückgegeben stderrwird. Wenn Sie also darauf zugreifen [0], greifen Sie einfach zu stdout. Sie könnten auch tun text, err = pipe.communicate()und werden dann texthaben, was Sie erwarten
Jona
23

Angenommen, dies pwdist nur ein Beispiel, so können Sie es tun:

import subprocess

p = subprocess.Popen("pwd", stdout=subprocess.PIPE)
result = p.communicate()[0]
print result

Ein weiteres Beispiel und weitere Informationen finden Sie in der Dokumentation zum Unterprozess .

Mark Byers
quelle
22

subprocess.Popen: http://docs.python.org/2/library/subprocess.html#subprocess.Popen

import subprocess

command = "ntpq -p"  # the shell command
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=None, shell=True)

#Launch the shell command:
output = process.communicate()

print output[0]

Im Popen Konstruktor, wenn Shell ist wahr , sollten Sie den Befehl als String und nicht als Folge passieren. Andernfalls teilen Sie den Befehl einfach in eine Liste auf:

command = ["ntpq", "-p"]  # the shell command
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=None)

Wenn Sie auch den Standardfehler in die Popen-Initialisierung einlesen müssen, können Sie stderr auf subprocess.PIPE oder subprocess.STDOUT setzen :

import subprocess

command = "ntpq -p"  # the shell command
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)

#Launch the shell command:
output, error = process.communicate()
Paolo Rovelli
quelle
genial das ist was ich gesucht habe
kRazzy R
14

Für Python 2.7+ lautet die idiomatische Antwort: subprocess.check_output()

Sie sollten auch die Behandlung von Argumenten beim Aufrufen eines Unterprozesses beachten, da dies etwas verwirrend sein kann.

Wenn args nur ein einzelner Befehl ohne eigene Argumente ist (oder Sie festgelegt haben shell=True), kann es sich um eine Zeichenfolge handeln. Ansonsten muss es eine Liste sein.

Zum Beispiel ... um den lsBefehl aufzurufen , ist dies in Ordnung:

from subprocess import check_call
check_call('ls')

also das ist:

from subprocess import check_call
check_call(['ls',])

Wenn Sie jedoch einige Argumente an den Shell-Befehl übergeben möchten, können Sie dies nicht tun:

from subprocess import check_call
check_call('ls -al')

Stattdessen müssen Sie es als Liste übergeben:

from subprocess import check_call
check_call(['ls', '-al'])

Die shlex.split()Funktion kann manchmal nützlich sein, um einen String in eine Shell-ähnliche Syntax aufzuteilen, bevor Unterprozesse erstellt werden ... wie folgt:

from subprocess import check_call
import shlex
check_call(shlex.split('ls -al'))
Corey Goldberg
quelle
Fünf Jahre später bekommt diese Frage immer noch viel Liebe. Danke für das 2.7+ Update, Corey!
Mark
11

Das funktioniert perfekt für mich:

import subprocess
try:
    #prints results and merges stdout and std
    result = subprocess.check_output("echo %USERNAME%", stderr=subprocess.STDOUT, shell=True)
    print result
    #causes error and merges stdout and stderr
    result = subprocess.check_output("copy testfds", stderr=subprocess.STDOUT, shell=True)
except subprocess.CalledProcessError, ex: # error code <> 0 
    print "--------error------"
    print ex.cmd
    print ex.message
    print ex.returncode
    print ex.output # contains stdout and stderr together 
Patrick Wolf
quelle
9

Das war perfekt für mich. Sie erhalten den Rückkehrcode stdout und stderr in einem Tupel.

from subprocess import Popen, PIPE

def console(cmd):
    p = Popen(cmd, shell=True, stdout=PIPE)
    out, err = p.communicate()
    return (p.returncode, out, err)

Beispielsweise:

result = console('ls -l')
print 'returncode: %s' % result[0]
print 'output: %s' % result[1]
print 'error: %s' % result[2]
gravmatt
quelle
6

Die akzeptierte Antwort ist immer noch gut, nur ein paar Anmerkungen zu neueren Funktionen. Da Python 3.6, können Sie kodieren , direkt in Griff check_output, siehe Dokumentation . Dies gibt jetzt ein String-Objekt zurück:

import subprocess 
out = subprocess.check_output(["ls", "-l"], encoding="utf-8")

In Python 3.7 capture_outputwurde subprocess.run () ein Parameter hinzugefügt, der einen Teil der Popen / PIPE-Behandlung für uns übernimmt. Weitere Informationen finden Sie in den Python-Dokumenten :

import subprocess 
p2 = subprocess.run(["ls", "-l"], capture_output=True, encoding="utf-8")
p2.stdout
ynux
quelle
4
 import os   
 list = os.popen('pwd').read()

In diesem Fall haben Sie nur ein Element in der Liste.

Alex
quelle
7
os.popenist zugunsten des subprocessModuls veraltet .
Mike Graham
3
Dies war sehr hilfreich für den Administrator einer alten Box mit der 2.2.X-Serie von Python.
Neil McF
4

Ich habe eine kleine Funktion geschrieben, die auf den anderen Antworten hier basiert:

def pexec(*args):
    return subprocess.Popen(args, stdout=subprocess.PIPE).communicate()[0].rstrip()

Verwendungszweck:

changeset = pexec('hg','id','--id')
branch = pexec('hg','id','--branch')
revnum = pexec('hg','id','--num')
print('%s : %s (%s)' % (revnum, changeset, branch))
mpen
quelle
4

In Python 3.7 wurde ein neues Schlüsselwortargument capture_outputfür eingeführt subprocess.run. Kurz und einfach aktivieren:

import subprocess

p = subprocess.run("echo 'hello world!'", capture_output=True, shell=True, encoding="utf8")
assert p.stdout == 'hello world!\n'
erb
quelle
1
import subprocess
output = str(subprocess.Popen("ntpq -p",shell = True,stdout = subprocess.PIPE, 
stderr = subprocess.STDOUT).communicate()[0])

Dies ist eine einzeilige Lösung

Kirti Kedia
quelle
0

Im Folgenden werden stdout und stderr des Prozesses in einer einzelnen Variablen erfasst. Es ist Python 2 und 3 kompatibel:

from subprocess import check_output, CalledProcessError, STDOUT

command = ["ls", "-l"]
try:
    output = check_output(command, stderr=STDOUT).decode()
    success = True 
except CalledProcessError as e:
    output = e.output.decode()
    success = False

Wenn Ihr Befehl eher eine Zeichenfolge als ein Array ist, stellen Sie Folgendes voran:

import shlex
command = shlex.split(command)
Zags
quelle
0

Für Python 3.5 habe ich eine Funktion basierend auf der vorherigen Antwort eingerichtet. Protokoll kann entfernt werden, fand es schön zu haben

import shlex
from subprocess import check_output, CalledProcessError, STDOUT


def cmdline(command):
    log("cmdline:{}".format(command))
    cmdArr = shlex.split(command)
    try:
        output = check_output(cmdArr,  stderr=STDOUT).decode()
        log("Success:{}".format(output))
    except (CalledProcessError) as e:
        output = e.output.decode()
        log("Fail:{}".format(output))
    except (Exception) as e:
        output = str(e);
        log("Fail:{}".format(e))
    return str(output)


def log(msg):
    msg = str(msg)
    d_date = datetime.datetime.now()
    now = str(d_date.strftime("%Y-%m-%d %H:%M:%S"))
    print(now + " " + msg)
    if ("LOG_FILE" in globals()):
        with open(LOG_FILE, "a") as myfile:
            myfile.write(now + " " + msg + "\n")
Pavel Niedoba
quelle
0

Verwenden Sie die ckeck_outputMethode vonsubprocess

import subprocess
address = 192.168.x.x
res = subprocess.check_output(['ping', address, '-c', '3'])

Zum Schluss analysieren Sie die Zeichenfolge

for line in res.splitlines():

Hoffe es hilft, fröhliche Codierung

Salman Mushtaq
quelle