Ich möchte eine Funktion schreiben, die einen Shell-Befehl ausführt und seine Ausgabe als Zeichenfolge zurückgibt , unabhängig davon, ob es sich um eine Fehler- oder Erfolgsmeldung handelt. Ich möchte nur das gleiche Ergebnis erzielen, das ich mit der Befehlszeile erzielt hätte.
Was wäre ein Codebeispiel, das so etwas tun würde?
Zum Beispiel:
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'
python
shell
subprocess
Silbernes Licht
quelle
quelle
Antworten:
Die Antwort auf diese Frage hängt von der von Ihnen verwendeten Python-Version ab. Der einfachste Ansatz ist die Verwendung der
subprocess.check_output
Funktion:check_output
führt ein einzelnes Programm aus, das nur Argumente als Eingabe verwendet. 1 Das Ergebnis wird genau so zurückgegeben, wie es gedruckt wurdestdout
. Wenn Sie Eingaben schreiben müssen, fahren Siestdin
mit den Abschnittenrun
oder fortPopen
. Wenn Sie komplexe Shell-Befehle ausführen möchten, lesen Sie den Hinweis amshell=True
Ende dieser Antwort.Die
check_output
Funktion funktioniert auf fast allen Versionen von Python, die noch weit verbreitet sind (2.7+). 2 Für neuere Versionen ist dies jedoch nicht mehr der empfohlene Ansatz.Moderne Versionen von Python (3.5 oder höher):
run
Wenn Sie Python 3.5 oder höher verwenden und keine Abwärtskompatibilität benötigen , wird die neue
run
Funktion empfohlen. Es bietet eine sehr allgemeine API auf hoher Ebene für dassubprocess
Modul. Übergeben Sie dassubprocess.PIPE
Flag an dasstdout
Schlüsselwortargument, um die Ausgabe eines Programms zu erfassen . Greifen Sie dann auf dasstdout
Attribut des zurückgegebenenCompletedProcess
Objekts zu:Der Rückgabewert ist ein
bytes
Objekt. Wenn Sie also eine richtige Zeichenfolge wünschen, müssen Siedecode
diese verwenden. Angenommen, der aufgerufene Prozess gibt eine UTF-8-codierte Zeichenfolge zurück:Dies kann alles zu einem Einzeiler komprimiert werden:
Wenn Sie Eingaben an den Prozess übergeben möchten
stdin
, übergeben Sie einbytes
Objekt an dasinput
Schlüsselwortargument:Sie können Fehler erfassen, indem Sie
stderr=subprocess.PIPE
(Capture toresult.stderr
) oderstderr=subprocess.STDOUT
(Capture toresult.stdout
zusammen mit der regulären Ausgabe) übergeben. Wenn die Sicherheit keineshell=True
Rolle spielt , können Sie auch komplexere Shell-Befehle ausführen, indem Sie wie in den folgenden Hinweisen beschrieben übergeben.Dies erhöht die Komplexität im Vergleich zur alten Vorgehensweise. Aber ich denke, es lohnt sich: Jetzt können Sie fast alles tun, was Sie brauchen, um die
run
Funktion alleine zu nutzen.Ältere Versionen von Python (2.7-3.4):
check_output
Wenn Sie eine ältere Version von Python verwenden oder eine bescheidene Abwärtskompatibilität benötigen, können Sie die
check_output
Funktion wahrscheinlich wie oben kurz beschrieben verwenden. Es ist seit Python 2.7 verfügbar.Es akzeptiert dieselben Argumente wie
Popen
(siehe unten) und gibt eine Zeichenfolge zurück, die die Ausgabe des Programms enthält. Der Anfang dieser Antwort enthält ein detaillierteres Verwendungsbeispiel. In Python 3.5 und höhercheck_output
entspricht dies der Ausführungrun
mitcheck=True
undstdout=PIPE
und der Rückgabe nur desstdout
Attributs.Sie können übergeben,
stderr=subprocess.STDOUT
um sicherzustellen, dass Fehlermeldungen in der zurückgegebenen Ausgabe enthalten sind. In einigen Versionen von Python kann das Übergebenstderr=subprocess.PIPE
ancheck_output
jedoch Deadlocks verursachen . Wenn die Sicherheit keineshell=True
Rolle spielt , können Sie auch komplexere Shell-Befehle ausführen, indem Sie wie in den folgenden Hinweisen beschrieben übergeben.Wenn Sie
stderr
Eingaben an den Prozess weiterleiten oder an diesen übergeben müssen,check_output
ist dies nicht der Aufgabe gewachsen. Siehe die folgendenPopen
Beispiele in diesem Fall.Komplexe Anwendungen und Legacy-Versionen von Python (2.6 und niedriger):
Popen
Wenn Sie eine umfassende Abwärtskompatibilität benötigen oder komplexere Funktionen als
check_output
die bereitgestellten benötigen , müssen Sie direkt mitPopen
Objekten arbeiten, die die Low-Level-API für Unterprozesse kapseln.Der
Popen
Konstruktor akzeptiert entweder einen einzelnen Befehl ohne Argumente oder eine Liste mit einem Befehl als erstem Element, gefolgt von einer beliebigen Anzahl von Argumenten, die jeweils als separates Element in der Liste aufgeführt sind.shlex.split
kann helfen, Zeichenfolgen in entsprechend formatierte Listen zu analysieren.Popen
Objekte akzeptieren auch eine Vielzahl unterschiedlicher Argumente für die Prozess-E / A-Verwaltung und die Konfiguration auf niedriger Ebene.Das Senden und Erfassen von Ausgaben
communicate
ist fast immer die bevorzugte Methode. Wie in:Oder
Wenn Sie festlegen
stdin=PIPE
,communicate
können Sie auch Daten an den Prozess übergeben überstdin
:Hinweis Aaron Hall Antwort , die das auf einigen Systemen gibt, können Sie festlegen müssen
stdout
,stderr
undstdin
allePIPE
(oderDEVNULL
) zu erhalten ,communicate
überhaupt zu arbeiten.In einigen seltenen Fällen benötigen Sie möglicherweise eine komplexe Ausgabeerfassung in Echtzeit. Die Antwort von Vartec schlägt einen Weg nach vorne vor, aber andere Methoden als diese
communicate
neigen zu Deadlocks, wenn sie nicht sorgfältig angewendet werden.Wie bei allen oben genannten Funktionen können Sie komplexere Shell-Befehle ausführen, wenn die Sicherheit keine Rolle spielt
shell=True
.Anmerkungen
1. Shell-Befehle ausführen: das
shell=True
ArgumentNormalerweise wird jeder Anruf
run
,check_output
oder derPopen
Konstruktor führt ein einziges Programm . Das bedeutet, dass es keine ausgefallenen Pfeifen im Bash-Stil gibt. Wenn Sie komplexe Shell-Befehle ausführen möchten, können Sie übergebenshell=True
, was alle drei Funktionen unterstützen.Dies wirft jedoch Sicherheitsbedenken auf . Wenn Sie mehr als nur Light Scripting ausführen, ist es möglicherweise besser, jeden Prozess einzeln aufzurufen und die Ausgabe von jedem als Eingabe an die nächste über zu übergeben
Oder
Die Versuchung, Rohre direkt anzuschließen, ist groß; widerstehen. Andernfalls werden Sie wahrscheinlich Deadlocks sehen oder Hacky Dinge wie zu tun dies .
2. Unicode-Überlegungen
check_output
Gibt eine Zeichenfolge in Python 2 zurück, aber einbytes
Objekt in Python 3. Es lohnt sich, sich einen Moment Zeit zu nehmen , um etwas über Unicode zu lernen, wenn Sie dies noch nicht getan haben.quelle
check_output()
als auchcommunicate()
Sie müssen warten, bis der Vorgang abgeschlossen ist, undpoll()
Sie erhalten die Ausgabe so wie sie kommt. Kommt wirklich darauf an was du brauchst.out
war<class 'bytes'>
für mich vom Typ . Um die Ausgabe als Zeichenfolge zu erhalten, musste ich sie vor dem Drucken wieout.decode("utf-8")
shell=True
? Für mich geht das. Sie brauchen nicht,shlex.split
wenn Sie passierenshell=True
.shlex.split
ist für Nicht-Shell-Befehle. Ich denke, ich werde das bisschen rausnehmen, weil das das Wasser trübt.universal_newlines=True
dem Sie Unicode-Zeichenfolgen in der Standardcodierung des Systems übergeben und entfernen können . In 3.7 wurde dies in vernünftiger umbenannttext=True
.encoding
Parameter verwenden,subprocess.run
anstattresult.stdout.decode('utf-8')
ihn zu verwendensubprocess.run(['ls', '-l'], stdout=subprocess.PIPE, encoding='utf-8')
.Dies ist viel einfacher, funktioniert aber nur unter Unix (einschließlich Cygwin) und Python2.7.
Es gibt ein Tupel mit (return_value, output) zurück.
Verwenden Sie für eine Lösung, die sowohl in Python2 als auch in Python3 funktioniert,
subprocess
stattdessen das Modul:quelle
So ähnlich:
Beachten Sie, dass ich stderr nach stdout umleite. Möglicherweise ist es nicht genau das, was Sie möchten, aber ich möchte auch Fehlermeldungen.
Diese Funktion liefert zeilenweise (normalerweise müssen Sie warten, bis der Unterprozess abgeschlossen ist, um die Ausgabe als Ganzes zu erhalten).
Für Ihren Fall wäre die Verwendung:
quelle
wait
undcall
Funktionen zu vermeiden .PIPE
Wert fürstdin
diese Datei anzugeben und sie zu schließen, sobald siePopen
zurückgegeben wird.retcode
ist0
. Die Prüfung sollte seinif retcode is not None
. Sie sollten keine leeren Zeichenfolgen ausgeben (selbst eine leere Zeile besteht aus mindestens einem Symbol '\ n') :if line: yield line
. Rufen Siep.stdout.close()
am Ende an..readlines()
kehrt erst zurück, wenn alle Ausgaben gelesen wurden, und bricht daher bei großen Ausgaben ab, die nicht in den Speicher passen. Um zu vermeiden, dass gepufferte Daten nach dem Beenden desif retcode is not None: yield from p.stdout.readlines(); break
Die Antwort von Vartec liest nicht alle Zeilen, deshalb habe ich eine Version erstellt, die Folgendes tat:
Die Verwendung entspricht der akzeptierten Antwort:
quelle
return iter(p.stdout.readline, b'')
anstelle der while-Schleife verwendenp.stdout.readline()
kann die nicht leere zuvor gepufferte Ausgabe zurückgeben, auch wenn der untergeordnete Prozess bereits beendet wurde (p.poll()
nichtNone
).Dies ist eine knifflige, aber supereinfache Lösung, die in vielen Situationen funktioniert:
Mit der Ausgabe des Befehls wird eine temporäre Datei (hier ist tmp) erstellt, aus der Sie die gewünschte Ausgabe lesen können.
Zusätzlicher Hinweis aus den Kommentaren: Sie können die tmp-Datei bei einmaligen Aufträgen entfernen. Wenn Sie dies mehrmals tun müssen, müssen Sie den tmp nicht löschen.
quelle
mktemp
, damit es in Thread-Situationen funktioniert, denke ichos.remove('tmp')
, um sie "fileless" zu machen.Ich hatte das gleiche Problem, fand aber einen sehr einfachen Weg, dies zu tun:
Hoffe es hilft aus
Hinweis: Diese Lösung ist Python3-spezifisch, da
subprocess.getoutput()
sie in Python2 nicht funktioniertquelle
Sie können die folgenden Befehle verwenden, um einen beliebigen Shell-Befehl auszuführen. Ich habe sie auf Ubuntu verwendet.
Hinweis: Dies ist seit Python 2.6 veraltet. Jetzt müssen Sie verwenden
subprocess.Popen
. Unten ist das Beispielquelle
os.popen()
ist es in Python 2.6 veraltet, in Python 3.x jedoch nicht veraltet, da es in 3.x mit implementiert wirdsubprocess.Popen()
.Ihr Kilometerstand kann variieren. Ich habe versucht, @ senderles Version von Vartecs Lösung in Windows unter Python 2.6.5 zu testen, aber es wurden Fehler angezeigt, und es funktionierten keine anderen Lösungen. Mein Fehler war :
WindowsError: [Error 6] The handle is invalid
.Ich stellte fest, dass ich jedem Handle PIPE zuweisen musste, damit es die erwartete Ausgabe zurückgab - das Folgende funktionierte für mich.
und rufe so auf (
[0]
bekommt das erste Element des Tupelsstdout
):Nachdem ich mehr gelernt habe, glaube ich, dass ich diese Pipe-Argumente brauche, weil ich an einem benutzerdefinierten System arbeite, das verschiedene Handles verwendet, also musste ich alle Standards direkt steuern.
Gehen Sie folgendermaßen vor, um Konsolen-Popups (unter Windows) zu stoppen:
quelle
DEVNULL
anstelle von ,subprocess.PIPE
wenn Sie nicht schreiben / lesen aus einem Rohr sonst können Sie das Kind Prozess hängen.Ich hatte einen etwas anderen Geschmack des gleichen Problems mit den folgenden Anforderungen:
obigen Schlüsselwort'ield '
Ich habe frühere Antworten kombiniert und optimiert, um Folgendes zu erhalten:
Dieser Code würde genauso ausgeführt wie die vorherigen Antworten:
quelle
p.poll()
zwischen den Aufrufen ohne Schlaf weiter telefonieren würden, würden wir CPU-Zyklen verschwenden, indem wir diese Funktion millionenfach aufrufen. Stattdessen "drosseln" wir unsere Schleife, indem wir dem Betriebssystem mitteilen, dass wir uns nicht um die nächste 1/10 Sekunde kümmern müssen, damit es andere Aufgaben ausführen kann. (Es ist möglich, dass p.poll () auch schläft, wodurch unsere Schlafanweisung überflüssig wird.)Das Aufteilen des Anfangsbefehls für das
subprocess
kann schwierig und umständlich sein.Verwenden Sie
shlex.split()
, um sich selbst zu helfen.Beispielbefehl
git log -n 5 --since "5 years ago" --until "2 year ago"
Der Code
Ohne
shlex.split()
den Code würde wie folgt aussehenquelle
shlex.split()
Dies ist eine Annehmlichkeit, insbesondere wenn Sie nicht wissen, wie genau das Zitieren in der Shell funktioniert. Das manuelle Konvertieren dieser Zeichenfolge in die Liste['git', 'log', '-n', '5', '--since', '5 years ago', '--until', '2 year ago']
ist jedoch überhaupt nicht schwierig, wenn Sie das Zitieren verstehen.Wenn Sie einen Shell-Befehl für mehrere Dateien ausführen müssen, hat dies den Trick für mich getan.
Bearbeiten: Ich habe gerade die Lösung von Max Persson mit dem Vorschlag von JF Sebastian gesehen. Ging voran und nahm das auf.
quelle
Popen
Akzeptiert entweder eine Zeichenfolge, aber dann benötigen Sieshell=True
oder eine Liste von Argumenten. In diesem Fall sollten Sie['nm', filename]
anstelle einer Zeichenfolge übergeben. Letzteres ist vorzuziehen, da die Shell die Komplexität erhöht, ohne hier einen Wert bereitzustellen. Das Übergeben einer Zeichenfolge ohneshell=True
funktioniert anscheinend unter Windows, dies kann sich jedoch in jeder nächsten Python-Version ändern.Laut @senderle, wenn Sie Python3.6 wie ich verwenden:
Verhält sich genau so, wie Sie den Befehl in bash ausführen
quelle
check=True, universal_newlines=True
. Mit anderen Worten, machtsubprocess.run()
schon alles, was Ihr Code macht.Wenn Sie das
subprocess
Python-Modul verwenden, können Sie den Befehlscode STDOUT, STDERR und return separat behandeln. Sie können ein Beispiel für die vollständige Implementierung des Befehlsaufrufers sehen. Natürlich können Sie es erweitern,try..except
wenn Sie möchten.Die folgende Funktion gibt den STDOUT-, STDERR- und Return-Code zurück, damit Sie sie im anderen Skript verarbeiten können.
quelle
subprocess.run()
. Das Rad nicht neu erfinden.Beispiel: Führen Sie ('ls -ahl') differenzierte drei / vier mögliche Rückgaben und Betriebssystemplattformen aus:
Funktion unten
quelle
Die Ausgabe kann in eine Textdatei umgeleitet und dann zurückgelesen werden.
quelle
stdout=temp_file
?ecls.exe
scheint das ein anderes Befehlszeilentool bereitzustellen, sodass der einfache Weg manchmal nicht funktioniert hat.habe gerade ein kleines Bash-Skript geschrieben, um dies mit Curl zu tun
https://gist.github.com/harish2704/bfb8abece94893c53ce344548ead8ba5
quelle