starten - Aufruf eines externen Befehls in Python




subprocess python 3 (20)

Aufruf eines externen Befehls in Python

Verwenden Sie einfach subprocess.run , das ein CompletedProcess Objekt zurückgibt:

>>> import subprocess
>>> completed_process = subprocess.run('python --version')
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
>>> completed_process
CompletedProcess(args='python --version', returncode=0)

Warum?

Ab Python 3.5 empfiehlt die Dokumentation subprocess.run :

Die empfohlene Methode zum Aufrufen von Unterprozessen ist die Verwendung der run () - Funktion für alle Anwendungsfälle, die damit behandelt werden können. Für weitergehende Anwendungsfälle kann die zugrunde liegende Popen-Schnittstelle direkt verwendet werden.

Hier ist ein Beispiel für die einfachste Verwendung - und genau wie gewünscht:

>>> import subprocess
>>> completed_process = subprocess.run('python --version')
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
>>> completed_process
CompletedProcess(args='python --version', returncode=0)

run wartet auf den erfolgreichen Abschluss des Befehls und gibt dann ein CompletedProcess Objekt zurück. Es kann stattdessen TimeoutExpired (wenn Sie ein timeout= Argument CalledProcessError ) oder CalledProcessError (wenn es fehlschlägt und Sie check=True ) CalledProcessError .

Wie Sie aus dem obigen Beispiel entnehmen können, werden stdout und stderr standardmäßig an Ihren eigenen stdout und stderr weitergeleitet.

Wir können das zurückgegebene Objekt überprüfen und den gegebenen Befehl sowie den Rückkehrcode sehen:

>>> completed_process.args
'python --version'
>>> completed_process.returncode
0

Ausgabe erfassen

Wenn Sie die Ausgabe erfassen möchten, können Sie subprocess.PIPE an das entsprechende stderr oder stdout :

>>> cp = subprocess.run('python --version', 
                        stderr=subprocess.PIPE, 
                        stdout=subprocess.PIPE)
>>> cp.stderr
b'Python 3.6.1 :: Anaconda 4.4.0 (64-bit)\r\n'
>>> cp.stdout
b''

(Ich finde es interessant und etwas uninteressant, dass die Versionsinformationen statt stdout auf stderr gestellt werden.)

Übergeben Sie eine Befehlsliste

Man könnte leicht von der manuellen Eingabe einer Befehlszeichenfolge (wie in der Frage vorgeschlagen wird) zu einer programmgesteuerten Zeichenfolge übergehen. Erstellen Sie keine Strings programmgesteuert. Dies ist ein potenzielles Sicherheitsproblem. Es ist besser anzunehmen, dass Sie der Eingabe nicht vertrauen.

>>> import textwrap
>>> args = ['python', textwrap.__file__]
>>> cp = subprocess.run(args, stdout=subprocess.PIPE)
>>> cp.stdout
b'Hello there.\r\n  This is indented.\r\n'

Beachten Sie, dass nur args positionell übergeben werden sollten.

Vollständige Unterschrift

Hier ist die tatsächliche Signatur in der Quelle und wie in help(run) :

def run(*popenargs, input=None, timeout=None, check=False, **kwargs):

Die popenargs und kwargs werden dem Popen Konstruktor übergeben. input kann eine Zeichenfolge von Bytes sein (oder Unicode, wenn encoding oder universal_newlines=True ), die an die Standardeingabe des Unterprozesses geleitet wird.

Die Dokumentation beschreibt timeout= und check=True besser als ich könnte:

Das Timeout-Argument wird an Popen.communicate () übergeben. Wenn das Zeitlimit abgelaufen ist, wird der untergeordnete Prozess beendet und gewartet. Die TimeoutExpired-Ausnahme wird erneut ausgelöst, nachdem der untergeordnete Prozess beendet wurde.

Wenn check den Wert true hat und der Prozess mit einem Exit-Code ungleich Null beendet wird, wird eine CalledProcessError-Ausnahme ausgelöst. Attribute dieser Ausnahme enthalten die Argumente, den Exit-Code und stdout und stderr, wenn sie erfasst wurden.

und dieses Beispiel für check=True ist besser als eines, das ich mir vorstellen 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 in der Dokumentation angegeben:

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 Argumentliste positionell übergeben werden soll. Übergeben Sie die restlichen Argumente als Schlüsselwortargumente.

Popen

Wann Popenstattdessen verwenden? Ich würde kaum einen Anwendungsfall finden, der allein auf den Argumenten basiert. Direkte Verwendung von Popenwürde Ihnen jedoch Zugriff auf seine Methoden gewähren, einschließlich poll"send_signal", "terminate" und "wait".

Hier ist die PopenSignatur wie in der Quelle angegeben . Ich denke, das 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=_PLATFORM_DEFAULT_CLOSE_FDS,
             shell=False, cwd=None, env=None, universal_newlines=False,
             startupinfo=None, creationflags=0,
             restore_signals=True, start_new_session=False,
             pass_fds=(), *, encoding=None, errors=None):

Aber informativer ist die PopenDokumentation :

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=False,
                 startupinfo=None, creationflags=0, restore_signals=True,
                 start_new_session=False, pass_fds=(), *, encoding=None, errors=None)

Führen Sie ein untergeordnetes Programm in einem neuen Prozess aus. Unter POSIX verwendet die Klasse os.execvp () - ähnliches Verhalten, um das untergeordnete Programm auszuführen. Unter Windows verwendet die Klasse die Windows-Funktion CreateProcess (). Die Argumente für Popen lauten wie folgt.

Das Verständnis der restlichen Dokumentation ist Popendem Leser als Übung überlassen.

https://code.i-harness.com

Wie kann ich einen externen Befehl (als hätte ich ihn an der Unix-Shell oder die Windows-Eingabeaufforderung eingegeben) in einem Python-Skript aufrufen?


Aktualisieren:

subprocess.run ist der empfohlene Ansatz ab Python 3.5, wenn der Code die Kompatibilität mit früheren Python-Versionen nicht aufrechterhalten muss. Es ist konsistenter und bietet eine ähnliche Benutzerfreundlichkeit wie Envoy. (Piping ist jedoch nicht so einfach. Sehen Sie sich diese Frage an .)

Hier einige Beispiele aus den Dokumenten .

Einen Prozess ausführen:

>>> subprocess.run(["ls", "-l"])  # doesn't capture output
CompletedProcess(args=['ls', '-l'], returncode=0)

Bei fehlgeschlagenem Lauf erhöhen:

>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
  ...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1

Capture-Ausgabe:

>>> subprocess.run(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE)
CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0,
stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n')

Ursprüngliche Antwort:

Ich empfehle den https://github.com/kennethreitz/envoy . Es ist ein Wrapper für einen Teilprozess, der die älteren Module und Funktionen ersetzen soll . Gesandter ist Unterprozess für Menschen.

Beispielanwendung aus der Readme :

>>> r = envoy.run('git config', data='data to pipe in', timeout=2)

>>> r.status_code
129
>>> r.std_out
'usage: git config [options]'
>>> r.std_err
''

Rohrzeug auch herum:

>>> r = envoy.run('uptime | pbcopy')

>>> r.command
'pbcopy'
>>> r.status_code
0

>>> r.history
[<Response 'uptime'>]

Überprüfen Sie auch die Python-Bibliothek "pexpect".

Es ermöglicht die interaktive Steuerung externer Programme / Befehle, auch ssh, ftp, telnet usw. Sie können einfach Folgendes eingeben:

child = pexpect.spawn('ftp 192.168.0.24')

child.expect('(?i)name .*: ')

child.sendline('anonymous')

child.expect('(?i)password')

Benutzen:

import os

cmd = 'ls -al'

os.system(cmd)

os - Dieses Modul bietet eine tragbare Möglichkeit, betriebssystemabhängige Funktionen zu verwenden.

Für die weiteren os finden Sie here die Dokumentation.


Es gibt auch Plumbum

>>> from plumbum import local
>>> ls = local["ls"]
>>> ls
LocalCommand(<LocalPath /bin/ls>)
>>> ls()
u'build.py\ndist\ndocs\nLICENSE\nplumbum\nREADME.rst\nsetup.py\ntests\ntodo.txt\n'
>>> notepad = local["c:\\windows\\notepad.exe"]
>>> notepad()                                   # Notepad window pops up
u''                                             # Notepad window is closed by user, command returns

Es gibt viele verschiedene Bibliotheken, mit denen Sie externe Befehle mit Python aufrufen können. Für jede Bibliothek habe ich eine Beschreibung gegeben und ein Beispiel für das Aufrufen eines externen Befehls gezeigt. Der Befehl, den ich als Beispiel verwendet habe, ist ls -l (alle Dateien ls -l ). Wenn Sie mehr über eine der Bibliotheken erfahren möchten, habe ich die Dokumentation für jede einzelne aufgelistet und verlinkt.

Quellen:

Dies sind alle Bibliotheken:

Hoffentlich hilft Ihnen das bei der Entscheidung, welche Bibliothek verwendet werden soll :)

Subprozess

Mit dem Unterprozess können Sie externe Befehle aufrufen und sie mit ihren Eingabe- / Ausgabe- / Fehler-Pipes (stdin, stdout und stderr) verbinden. Unterprozess ist die Standardeinstellung für die Ausführung von Befehlen, aber manchmal sind andere Module besser.

subprocess.run(["ls", "-l"]) # Run command
subprocess.run(["ls", "-l"], stdout=subprocess.PIPE) # This will run the command and return any output
subprocess.run(shlex.split("ls -l")) # You can also use the shlex library to split the command

os

os wird für "betriebssystemabhängige Funktionalität" verwendet. Es kann auch verwendet werden, um externe Befehle mit os.system und os.popen (Hinweis: Es gibt auch einen Unterprozess.popen). os führt immer die Shell aus und ist eine einfache Alternative für Benutzer, die subprocess.run nicht benötigen oder nicht wissen.

os.system("ls -l") # run command
os.popen("ls -l").read() # This will run the command and return any output

Sch

sh ist eine Unterprozessschnittstelle, mit der Sie Programme so aufrufen können, als wären sie Funktionen. Dies ist nützlich, wenn Sie einen Befehl mehrmals ausführen möchten.

sh.ls("-l") # Run command normally
ls_cmd = sh.Command("ls") # Save command as a variable
ls_cmd() # Run command as if it were a function

Plumbum

plumbum ist eine Bibliothek für "scriptähnliche" Python-Programme. Sie können Programme wie Funktionen wie in sh aufrufen. Plumbum ist nützlich, wenn Sie eine Pipeline ohne Shell ausführen möchten.

ls_cmd = plumbum.local("ls -l") # get command
ls_cmd() # run command

pexpect

Mit pexpect können Sie untergeordnete Anwendungen erzeugen, sie steuern und Muster in ihrer Ausgabe finden. Dies ist eine bessere Alternative zum Unterprozess für Befehle, die unter Unix ein tty erwarten.

pexpect.run("ls -l") # Run command as normal
child = pexpect.spawn('scp foo [email protected]:.') # Spawns child application
child.expect('Password:') # When this is the output
child.sendline('mypassword')

Stoff

Fabric ist eine Python 2.5- und 2.7-Bibliothek. Sie können lokale und Remote-Shell-Befehle ausführen. Fabric ist eine einfache Alternative zum Ausführen von Befehlen in einer sicheren Shell (SSH).

fabric.operations.local('ls -l') # Run command as normal
fabric.operations.local('ls -l', capture = True) # Run command and receive output

Gesandte

Gesandter ist als "Subprozess für Menschen" bekannt. Es wird als praktischer Wrapper für das subprocess Modul verwendet.

r = envoy.run("ls -l") # Run command
r.std_out # get output

Befehle

commands enthalten Wrapper-Funktionen für os.popen , wurden jedoch aus Python 3 entfernt, da subprocess eine bessere Alternative ist.

Die Bearbeitung basierte auf JF Sebastians Kommentar.


Hier ist eine Zusammenfassung der Möglichkeiten, externe Programme aufzurufen, sowie deren Vor- und Nachteile:

  1. os.system("some_command with args") übergibt den Befehl und die Argumente an die Shell Ihres Systems. Das ist schön, weil Sie auf diese Weise tatsächlich mehrere Befehle gleichzeitig ausführen und Pipes und Eingabe- / Ausgabeumleitungen einrichten können. Zum Beispiel:

    os.system("some_command < input_file | another_command > output_file")  
    

    Das ist zwar praktisch, Sie müssen jedoch das Fluchtverhalten von Shell-Zeichen wie Leerzeichen usw. manuell handhaben. Auf der anderen Seite können Sie auch Befehle ausführen, bei denen es sich lediglich um Shell-Befehle handelt und nicht um externe Programme. Siehe die Dokumentation .

  2. stream = os.popen("some_command with args") os.system wie os.system außer dass Sie ein dateiähnliches Objekt erhalten, mit dem Sie auf die Standardein- / ausgabe für diesen Prozess zugreifen können. Es gibt 3 weitere Varianten von popen, die alle mit der E / A etwas anders umgehen. Wenn Sie alles als String übergeben, wird Ihr Befehl an die Shell übergeben. Wenn Sie sie als Liste übergeben, müssen Sie sich keine Sorgen darüber machen, dass irgendetwas entkommt. Siehe die Dokumentation .

  3. Die Popen Klasse des subprocess Moduls. Dies ist als Ersatz für os.popen , hat aber den Nachteil, dass es aufgrund seiner umfassenden os.popen etwas komplizierter ist. Zum Beispiel würden Sie sagen:

    print subprocess.Popen("echo Hello World", shell=True, stdout=subprocess.PIPE).stdout.read()
    

    anstatt:

    print os.popen("echo Hello World").read()
    

    Aber es ist schön, alle Optionen in einer einheitlichen Klasse zu haben, anstatt 4 verschiedene Popen-Funktionen. Siehe die Dokumentation .

  4. Die call vom subprocess . Dies ist im Grunde genauso wie die Popen Klasse und akzeptiert alle gleichen Argumente. Sie wartet jedoch einfach, bis der Befehl abgeschlossen ist, und gibt den Rückkehrcode zurück. Zum Beispiel:

    return_code = subprocess.call("echo Hello World", shell=True)  
    

    Siehe die Dokumentation .

  5. Wenn Sie Python 3.5 oder höher verwenden, können Sie die neue Funktion subprocess.run verwenden. subprocess.run Funktion ähnelt der obigen Beschreibung, ist jedoch noch flexibler und gibt ein CompletedProcess Objekt zurück, wenn der Befehl ausgeführt wird.

  6. Das os-Modul verfügt auch über alle Fork- / Exec- / Spawn-Funktionen, die Sie in einem C-Programm haben würden, aber ich empfehle nicht, sie direkt zu verwenden.

Das subprocess Modul sollte wahrscheinlich das sein, was Sie verwenden.

Beachten Sie schließlich, dass Sie für alle Methoden, bei denen Sie den endgültigen Befehl übergeben, der von der Shell als String ausgeführt werden soll, die Verantwortung dafür übernehmen müssen. Es gibt schwerwiegende Auswirkungen auf die Sicherheit, wenn einem Teil der Zeichenfolge, den Sie übergeben, nicht vollständig vertraut werden kann. Zum Beispiel, wenn ein Benutzer einen oder einen Teil der Zeichenfolge eingibt. Wenn Sie sich nicht sicher sind, verwenden Sie diese Methoden nur mit Konstanten. Um einen Hinweis auf die Auswirkungen zu geben, beachten Sie diesen Code:

print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()

und stellen Sie sich vor, der Benutzer gibt "Meine Mutter hat mich nicht geliebt" & rm -rf / ein.


Ich benutze immer fabric für diese Dinge wie:

from fabric.operations import local
result = local('ls', capture=True)
print "Content:/n%s" % (result, )

Dies scheint jedoch ein gutes Werkzeug zu sein: sh (Python Subprozess Interface) .

Schauen Sie sich ein Beispiel an:

from sh import vgdisplay
print vgdisplay()
print vgdisplay('-v')
print vgdisplay(v=True)

Ich würde empfehlen, das Subprozess-Modul anstelle von os.system zu verwenden, da es für Sie Shell-Flucht macht und daher viel sicherer ist: http://docs.python.org/library/subprocess.html

subprocess.call(['ping', 'localhost'])

Ohne Ausgabe des Ergebnisses:

import os
os.system("your command here")

Mit Ausgabe des Ergebnisses:

import commands
commands.getoutput("your command here")
or
commands.getstatusoutput("your command here")

So führe ich meine Befehle aus. Dieser Code hat alles, was Sie so ziemlich brauchen

from subprocess import Popen, PIPE
cmd = "ls -l ~/"
p = Popen(cmd , shell=True, stdout=PIPE, stderr=PIPE)
out, err = p.communicate()
print "Return code: ", p.returncode
print out.rstrip(), err.rstrip()

Wenn Sie die Ausgabe des Befehls benötigen, den Sie aufrufen, können Sie subprocess.check_output (Python 2.7+) verwenden.

>>> subprocess.check_output(["ls", "-l", "/dev/null"])
'crw-rw-rw- 1 root root 1, 3 Oct 18  2007 /dev/null\n'

Beachten Sie auch den shell Parameter.

Wenn die Shell True , wird der angegebene Befehl über die Shell ausgeführt. Dies kann nützlich sein, wenn Sie Python hauptsächlich für den verbesserten Steuerungsfluss verwenden, den es über die meisten System-Shells bietet, und trotzdem bequemen Zugriff auf andere Shell-Funktionen wie Shell-Pipes, Dateinamens-Platzhalter, Erweiterung der Umgebungsvariablen und die Erweiterung von ~ zu Hause eines Benutzers wünschen Verzeichnis. Beachten Sie jedoch, dass Python selbst Implementierungen vieler shell-ähnlicher Features bietet (insbesondere fnmatch , os.walk() , os.walk() , os.path.expandvars() , os.path.expanduser() und shutil ).


os.system ist in Ordnung, aber irgendwie veraltet. Es ist auch nicht sehr sicher. Versuchen Sie stattdessen einen subprocess . os.system ruft sh nicht direkt auf und ist daher sicherer als os.system .

Weitere Informationen erhalten Sie here .


os.systemEs ist nicht möglich, Ergebnisse zu speichern, wenn Sie also Ergebnisse in einer Liste speichern möchten oder etwas subprocess.callfunktioniert.


Es gibt hier einen weiteren Unterschied, der zuvor nicht erwähnt wurde.

subprocess.Popenführt den <Befehl> als Unterprozess aus. In meinem Fall muss ich die Datei <a> ausführen, die mit einem anderen Programm, <b>, kommunizieren muss.

Ich habe einen Teilprozess ausprobiert und die Ausführung war erfolgreich. <B> konnte jedoch nicht mit <a> kommunizieren. Alles ist normal, wenn ich beide vom Terminal aus laufe.

Noch etwas mehr: (HINWEIS: kwrite verhält sich anders als andere Anwendungen. Wenn Sie Folgendes mit Firefox versuchen, werden die Ergebnisse nicht gleich sein.)

Wenn Sie es versuchen os.system("kwrite"), bleibt der Programmablauf stehen, bis der Benutzer kwrite beendet. Um das zu überwinden, versuchte ich es stattdessen os.system(konsole -e kwrite). Dieses Mal lief das Programm weiter, aber kwrite wurde zum Unterprozess der Konsole.

Jeder, der kwrite ausführt, ist kein Unterprozess (dh er muss im Systemmonitor am linken Rand des Baums erscheinen).


Sie können Popen verwenden und dann den Status der Prozedur überprüfen:

from subprocess import Popen

proc = Popen(['ls', '-l'])
if proc.poll() is None:
    proc.kill()

Sehen Sie sich subprocess.Popen .


Ich mag shell_command wegen seiner Einfachheit. Es ist auf dem Subprozess-Modul aufgebaut.

Hier ist ein Beispiel aus den Dokumenten:

>>> from shell_command import shell_call
>>> shell_call("ls *.py")
setup.py  shell_command.py  test_shell_command.py
0
>>> shell_call("ls -l *.py")
-rw-r--r-- 1 ncoghlan ncoghlan  391 2011-12-11 12:07 setup.py
-rw-r--r-- 1 ncoghlan ncoghlan 7855 2011-12-11 16:16 shell_command.py
-rwxr-xr-x 1 ncoghlan ncoghlan 8463 2011-12-11 16:17 test_shell_command.py
0

Ich neige dazu, Subprozess zusammen mit shlex (um das shlex von zitierten Strings zu handhaben):

>>> import subprocess, shlex
>>> command = 'ls -l "/your/path/with spaces/"'
>>> call_params = shlex.split(command)
>>> print call_params
["ls", "-l", "/your/path/with spaces/"]
>>> subprocess.call(call_params)

Schamloser Plug, ich habe dafür eine Bibliothek geschrieben: P https://github.com/houqp/shell.py

Es ist im Grunde ein Wrapper für Popen und Shlex für jetzt. Es unterstützt auch Piping-Befehle, damit Sie Befehle in Python einfacher verketten können. So kannst du Dinge tun wie:

ex('echo hello shell.py') | "awk '{print $2}'"

import os
cmd = 'ls -al'
os.system(cmd)

Wenn Sie die Ergebnisse des Befehls zurückgeben möchten, können Sie os.popen . Dies ist jedoch seit Version 2.6 zugunsten des Subprozess-Moduls veraltet, was andere Antworten gut abgedeckt haben.





external