Frage Aufruf eines externen Befehls in Python


Wie kann ich einen externen Befehl innerhalb eines Python-Skripts aufrufen (als ob ich ihn an der Unix-Shell oder der Windows-Eingabeaufforderung eingegeben hätte)?


3622
2017-09-18 01:35


Ursprung


Antworten:


Schaue auf die Unterprozessmodul in der Standardbibliothek:

from subprocess import call
call(["ls", "-l"])

Der Vorteil von Unterprozess gegen System ist, dass es flexibler ist (Sie können den stdout, stderr, den "echten" Statuscode, bessere Fehlerbehandlung, usw. bekommen).

Das offizielle Dokumentation empfiehlt die Unterprozess Modul über das alternative os.system ():

Das Unterprozess Modul bietet leistungsfähigere Möglichkeiten, neue Prozesse zu generieren und ihre Ergebnisse abzurufen; die Verwendung dieses Moduls ist der Verwendung dieser Funktion vorzuziehen [os.system()].

Das "Ersetzen älterer Funktionen durch das Subprozessmodul"Abschnitt in der Unterprozess Dokumentation kann einige hilfreiche Rezepte haben.

Offizielle Dokumentation über die Unterprozess Modul:


3491
2017-09-18 01:39



Hier finden Sie eine Zusammenfassung der Möglichkeiten, externe Programme aufzurufen, sowie die Vor- und Nachteile der einzelnen Programme:

  1. os.system("some_command with args") übergibt den Befehl und die Argumente an die Shell Ihres Systems. Das ist gut, weil Sie auf diese Weise mehrere Befehle gleichzeitig ausführen und Pipes und die Eingabe / Ausgabe-Umleitung einrichten können. Beispielsweise:

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

    Während dies jedoch praktisch ist, müssen Sie das Entweichen von Shell-Zeichen wie Leerzeichen usw. manuell handhaben. Auf der anderen Seite können Sie damit auch Befehle ausführen, die nur Shell-Befehle sind und keine eigentlichen externen Programme. Sehen die Dokumentation.

  2. stream = os.popen("some_command with args") wird das Gleiche tun wie os.system außer dass es Ihnen ein dateiähnliches Objekt gibt, mit dem Sie auf die Standardeingabe / -ausgabe für diesen Prozess zugreifen können. Es gibt 3 andere Varianten von Popen, die alle die I / O etwas anders handhaben. Wenn Sie alles als String übergeben, wird Ihr Befehl an die Shell übergeben. Wenn Sie sie als Liste übergeben, müssen Sie sich keine Gedanken darüber machen, ob Sie etwas entkommen können. Sehen die Dokumentation.

  3. Das Popen Klasse der subprocess Modul. Dies ist als Ersatz gedacht os.popen hat aber den Nachteil, dass es aufgrund seiner Vollständigkeit etwas komplizierter ist. Zum Beispiel würden Sie sagen:

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

    Anstatt von:

    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. Sehen die Dokumentation.

  4. Das call Funktion von der subprocess Modul. Das ist im Grunde genau wie das Popen Klasse und nimmt alle die gleichen Argumente, aber es wartet einfach bis der Befehl abgeschlossen ist und gibt Ihnen den Rückkehrcode. Beispielsweise:

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

    Sehen die Dokumentation.

  5. Wenn Sie Python 3.5 oder höher verwenden, können Sie das neue verwenden subprocess.run Funktion, die ähnlich wie oben ist, aber noch flexibler und gibt ein CompletedProcess Objekt, wenn der Befehl ausgeführt wird.

  6. Das OS-Modul hat auch alle fork / exec / spawn-Funktionen, die Sie in einem C-Programm haben, aber ich empfehle nicht, sie direkt zu verwenden.

Das subprocess Modul sollte wahrscheinlich sein, was Sie verwenden.

Bitte beachten Sie, dass für alle Methoden, bei denen Sie den letzten Befehl ausführen, der von der Shell als String ausgeführt wird, Sie dafür verantwortlich sind. Es gibt ernsthafte Auswirkungen auf die Sicherheit Wenn ein Teil der übergebenen Zeichenfolge nicht vollständig vertrauenswürdig ist. Zum Beispiel, wenn ein Benutzer einen Teil der Zeichenfolge eingibt. Wenn Sie sich nicht sicher sind, verwenden Sie diese Methoden nur mit Konstanten. Um Ihnen einen Eindruck von den Implikationen zu geben, betrachten Sie diesen Code:

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

und stellen Sie sich vor, dass der Benutzer "meine Mama hat mich nicht lieben" und "rm-rf /" eingibt.


2466
2017-09-18 13:11



Ich verwende normalerweise:

import subprocess

p = subprocess.Popen('ls', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in p.stdout.readlines():
    print line,
retval = p.wait()

Sie sind frei, zu tun, was Sie mit dem wollen stdout Daten in der Pipe. In der Tat können Sie diese Parameter einfach weglassen (stdout= und stderr=) und es wird sich verhalten os.system().


255
2017-09-18 18:20



Einige Hinweise zum Trennen des untergeordneten Prozesses vom aufrufenden (Starten des untergeordneten Prozesses im Hintergrund).

Angenommen, Sie möchten eine lange Aufgabe von einem CGI-Skript aus starten, dh der untergeordnete Prozess sollte länger als der CGI-Skript-Ausführungsprozess laufen.

Das klassische Beispiel aus den Subprozessmodul-Dokumenten ist:

import subprocess
import sys

# some code here

pid = subprocess.Popen([sys.executable, "longtask.py"]) # call subprocess

# some more code here

Die Idee hier ist, dass Sie nicht in der Zeile 'Subprocess aufrufen' warten wollen, bis die longtask.py beendet ist. Aber es ist nicht klar, was nach der Zeile "mehr Code hier" aus dem Beispiel passiert.

Meine Zielplattform war freebsd, aber die Entwicklung war auf Windows, so dass ich das Problem zuerst auf Windows stellte.

Unter Windows (win xp) wird der übergeordnete Prozess erst beendet, wenn longtask.py seine Arbeit beendet hat. Es ist nicht was Sie in CGI-Skript wollen. Das Problem ist nicht spezifisch für Python, in der PHP-Community sind die Probleme gleich.

Die Lösung besteht darin, DETACHED_PROCESS zu übergeben Prozess-Erstellungs-Flag auf die zugrunde liegende CreateProcess-Funktion in der win-API. Wenn Sie pywin32 installiert haben, können Sie das Flag aus dem win32process-Modul importieren, andernfalls sollten Sie es selbst definieren:

DETACHED_PROCESS = 0x00000008

pid = subprocess.Popen([sys.executable, "longtask.py"],
                       creationflags=DETACHED_PROCESS).pid

/ * UPD 2015.10.27 @eryksun in einem Kommentar unter Hinweise, dass das semantisch korrekte Flag CREATE_NEW_CONSOLE (0x00000010) * /

Auf freebsd haben wir ein anderes Problem: Wenn der Elternprozess beendet ist, beendet es auch die Kindprozesse. Und das wollen Sie auch nicht im CGI-Skript. Einige Experimente haben gezeigt, dass das Problem darin liegt, sys.stdout zu teilen. Und die Arbeitslösung war die folgende:

pid = subprocess.Popen([sys.executable, "longtask.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)

Ich habe den Code auf anderen Plattformen nicht überprüft und kenne die Gründe für das Verhalten auf freebsd nicht. Wenn jemand weiß, bitte teilen Sie Ihre Ideen. Beim Start von Hintergrundprozessen in Python zu googeln, ist noch kein Licht.


157
2018-02-12 10:15



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

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

97
2017-09-18 01:42



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

Wenn Sie die Ergebnisse des Befehls zurückgeben möchten, können Sie verwenden os.popen. Dies ist jedoch seit der Version 2.6 zugunsten der Unterprozessmodul, die andere Antworten gut abgedeckt haben.


93
2017-09-18 01:37



import os
os.system("your command")

Beachten Sie, dass dies gefährlich ist, da der Befehl nicht bereinigt wird. Ich überlasse es Ihnen, nach den relevanten Dokumentationen zu den Modulen 'os' und 'sys' zu suchen. Es gibt eine Reihe von Funktionen (exec * und spawn *), die ähnliche Dinge tun.


82
2017-09-18 01:37



Ich benutze immer fabric für diese Dinge wie:

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

Aber das scheint ein gutes Werkzeug zu sein: sh (Python-Subprozessschnittstelle).

Schauen Sie sich ein Beispiel an:

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

51
2018-03-13 00:12



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

Es ermöglicht die interaktive Steuerung von externen Programmen / Befehlen, sogar ssh, ftp, telnet, etc. Sie können einfach etwas eingeben wie:

child = pexpect.spawn('ftp 192.168.0.24')

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

child.sendline('anonymous')

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

51
2017-10-07 07:09