Ausführen eines Programms oder Aufrufen eines Systembefehls in Python
Einfach, verwenden subprocess.run
die eine CompletedProcess
Objekt:
>>> from subprocess import run
>>> from shlex import split
>>> completed_process = run(split('python --version'))
Python 3.8.8
>>> completed_process
CompletedProcess(args=['python', '--version'], returncode=0)
( run
will eine Liste von lexikalisch geparsten Shell-Argumenten - das ist das, was Sie in eine Shell eingeben würden, getrennt durch Leerzeichen, aber nicht dort, wo die Leerzeichen in Anführungszeichen stehen, also verwenden Sie eine spezielle Funktion, split
um das aufzuteilen, was Sie buchstäblich in Ihre Shell eingeben würden)
Warum?
Ab Python 3.5 empfiehlt die Dokumentation subprocess.run :
Der empfohlene Ansatz für den Aufruf von Unterprozessen ist die Verwendung der Funktion run() für alle Anwendungsfälle, die sie verarbeiten kann. Für fortgeschrittenere Anwendungsfälle kann die zugrunde liegende Popen-Schnittstelle direkt verwendet werden.
Hier ist ein Beispiel für die einfachste mögliche Verwendung - und sie tut genau das, was verlangt wird:
>>> from subprocess import run
>>> from shlex import split
>>> completed_process = run(split('python --version'))
Python 3.8.8
>>> completed_process
CompletedProcess(args=['python', '--version'], returncode=0)
run
wartet, bis der Befehl erfolgreich beendet wurde, und gibt dann ein CompletedProcess
Gegenstand. Stattdessen kann es TimeoutExpired
(wenn Sie ihm eine timeout=
Argument) oder CalledProcessError
(wenn er fehlschlägt und Sie bestehen check=True
).
Wie Sie aus dem obigen Beispiel ersehen können, werden stdout und stderr standardmäßig an Ihr eigenes stdout und stderr weitergeleitet.
Wir können das zurückgegebene Objekt untersuchen und den gegebenen Befehl und den Rückgabecode sehen:
>>> completed_process.args
['python', '--version']
>>> completed_process.returncode
0
Erfassen der Ausgabe
Wenn Sie die Ausgabe aufzeichnen möchten, können Sie subprocess.PIPE
an die entsprechende stderr
o stdout
:
>>> from subprocess import PIPE
>>> completed_process = run(shlex.split('python --version'), stdout=PIPE, stderr=PIPE)
>>> completed_process.stdout
b'Python 3.8.8\n'
>>> completed_process.stderr
b''
Und diese jeweiligen Attribute geben Bytes zurück.
Übergabe einer Befehlsliste
Man könnte leicht von der manuellen Eingabe eines Befehlsstrings (wie die Frage vorschlägt) zur Bereitstellung eines programmatisch erstellten Strings übergehen. Erstellen Sie Strings nicht programmatisch. Dies ist ein potenzielles Sicherheitsproblem. Es ist besser, davon auszugehen, dass Sie der Eingabe nicht trauen.
>>> import textwrap
>>> args = ['python', textwrap.__file__]
>>> cp = run(args, stdout=subprocess.PIPE)
>>> cp.stdout
b'Hello there.\n This is indented.\n'
Hinweis, nur args
sollte positionell übergeben werden.
Vollständige Unterschrift
Hier ist die tatsächliche Unterschrift in der Quelle und wie sie von help(run)
:
def run(*popenargs, input=None, timeout=None, check=False, **kwargs):
Die popenargs
y kwargs
werden an den Popen
Konstrukteur. input
kann eine Zeichenkette aus Bytes sein (oder Unicode, wenn die Kodierung oder universal_newlines=True
), die an die stdin des Unterprozesses weitergeleitet wird.
Die Dokumentation beschreibt timeout=
y check=True
besser als ich es könnte:
Das Argument timeout wird an Popen.communicate() übergeben. Wenn die Zeitüberschreitung abläuft, wird der Kindprozess beendet und es wird auf ihn gewartet. Die TimeoutExpired-Ausnahme wird erneut ausgelöst, nachdem der Kindprozess beendet wurde.
Wenn check true ist und der Prozess mit einem Exit-Code ungleich Null beendet wird, wird ein CalledProcessError-Ausnahme ausgelöst. Die Attribute dieser Ausnahme enthalten die Argumente, den Exit-Code sowie stdout und stderr, falls sie aufgezeichnet wurden.
und dieses Beispiel für check=True
ist besser als eine, die ich mir ausdenken könnte:
>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
Erweiterte Signatur
Hier ist eine erweiterte Signatur, wie sie in der Dokumentation angegeben ist:
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None,
shell=False, cwd=None, timeout=None, check=False, encoding=None,
errors=None)
Beachten Sie, dass dies bedeutet, dass nur die args-Liste positionell übergeben werden soll. Übergeben Sie also die restlichen Argumente als Schlüsselwortargumente.
Popen
Wenn Sie Popen
stattdessen? Ich würde mich schwer tun, allein auf der Grundlage der Argumente einen Anwendungsfall zu finden. Direkte Verwendung von Popen
würde Ihnen jedoch Zugang zu seinen Methoden geben, einschließlich poll
send_signal", "terminate" und "wait".
Hier ist die Popen
Unterschrift wie angegeben in die Quelle . Ich denke, dies ist die präziseste Verkapselung der Informationen (im Gegensatz zu help(Popen)
):
def __init__(self, args, bufsize=-1, executable=None,
stdin=None, stdout=None, stderr=None,
preexec_fn=None, close_fds=True,
shell=False, cwd=None, env=None, universal_newlines=None,
startupinfo=None, creationflags=0,
restore_signals=True, start_new_session=False,
pass_fds=(), *, user=None, group=None, extra_groups=None,
encoding=None, errors=None, text=None, umask=-1, pipesize=-1):
Informativer ist jedoch die Popen
Dokumentation :
subprocess.Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None,
stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None,
env=None, universal_newlines=None, startupinfo=None, creationflags=0,
restore_signals=True, start_new_session=False, pass_fds=(), *, group=None,
extra_groups=None, user=None, umask=-1, encoding=None, errors=None,
text=None)
Führen Sie ein untergeordnetes Programm in einem neuen Prozess aus. Unter POSIX verwendet die Klasse os.execvp()-ähnliches Verhalten, um das Kindprogramm auszuführen. Unter Windows, verwendet die Klasse die Windows-Funktion CreateProcess(). Die Argumente für Popen lauten wie folgt.
Verstehen der übrigen Dokumentation über Popen
wird als Übung für den Leser belassen.
0 Stimmen
Ich verstehe das nicht, was ist falsch an
import os; os.system('pip list | grep anatome')
? Zumindest kann man damit Piping machen, wie mein Beispiel zeigt. Es ist nicht klar, wie man das mitimport subprocess; subprocess.run(["ls", "-l"])
.