Ich versuche zu verstehen, was die Motivation für die Verwendung der Bibliotheksfunktionen von Python ist, um betriebssystemspezifische Aufgaben wie das Erstellen von Dateien / Verzeichnissen, das Ändern von Dateiattributen usw. auszuführen, anstatt diese Befehle nur über os.system()
oder auszuführen subprocess.call()
.
Warum sollte ich zum Beispiel verwenden, os.chmod
anstatt zu tun os.system("chmod...")
?
Ich verstehe, dass es "pythonischer" ist, die verfügbaren Bibliotheksmethoden von Python so oft wie möglich zu verwenden, anstatt nur Shell-Befehle direkt auszuführen. Aber gibt es eine andere Motivation, dies unter funktionalen Gesichtspunkten zu tun?
Ich spreche hier nur von der Ausführung einfacher einzeiliger Shell-Befehle. Wenn wir mehr Kontrolle über die Ausführung der Aufgabe benötigen, ist die Verwendung von subprocess
Modulen meines Erachtens beispielsweise sinnvoller.
quelle
print
wenn Sie könntenos.system("echo Hello world!")
?os.path
Pfade verwenden, anstatt sie manuell zu behandeln: Es funktioniert auf jedem Betriebssystem, auf dem es ausgeführt wird.os.chmod
wird daschmod
Programm, das die Shell ausführen würde, nicht aufrufen . Durchos.system('chmod ...')
Starten einer Shell wird eine Zeichenfolge interpretiert, um eine andere ausführbare Datei aufzurufen, um die C-chmod
Funktion aufzurufen , während die C- Funktionos.chmod(...)
viel direkter aufgerufen wirdchmod
.Antworten:
Es ist schneller ,
os.system
undsubprocess.call
neue Prozesse zu schaffen , die für etwas so einfach nicht notwendig ist. In der Tatos.system
undsubprocess.call
mit demshell
Argument erstellen Sie normalerweise mindestens zwei neue Prozesse: Der erste ist die Shell und der zweite ist der Befehl, den Sie ausführen (wenn es sich nicht um eine integrierte Shell handelttest
).Einige Befehle sind in einem separaten Prozess unbrauchbar . Wenn Sie beispielsweise ausführen
os.spawn("cd dir/")
, wird das aktuelle Arbeitsverzeichnis des untergeordneten Prozesses geändert, nicht jedoch des Python-Prozesses. Sie müssen dafür verwendenos.chdir
.Sie müssen sich keine Gedanken über Sonderzeichen machen , die von der Shell interpretiert werden.
os.chmod(path, mode)
funktioniert unabhängig vom Dateinamen, währendos.spawn("chmod 777 " + path)
es schrecklich fehlschlägt, wenn der Dateiname so etwas wie ist; rm -rf ~
. (Beachten Sie, dass Sie dies umgehen können, wenn Siesubprocess.call
ohne dasshell
Argument verwenden.)Sie müssen sich keine Gedanken über Dateinamen machen, die mit einem Bindestrich beginnen .
os.chmod("--quiet", mode)
ändert die Berechtigungen der genannten Datei--quiet
, schlägtos.spawn("chmod 777 --quiet")
jedoch fehl, da dies--quiet
als Argument interpretiert wird. Dies gilt auch fürsubprocess.call(["chmod", "777", "--quiet"])
.Sie haben weniger plattform- und Shell-übergreifende Probleme, da die Standardbibliothek von Python dies für Sie erledigen soll. Hat Ihr System einen
chmod
Befehl? Ist es installiert? Unterstützt es die Parameter, die Sie erwarten? Dasos
Modul wird versuchen, so plattformübergreifend wie möglich zu sein und dokumentiert, wann dies nicht möglich ist.Wenn der Befehl, den Sie ausführen, eine Ausgabe hat , die Sie interessiert, müssen Sie sie analysieren, was schwieriger ist als es sich anhört, da Sie möglicherweise Eckfälle (Dateinamen mit Leerzeichen, Tabulatoren und Zeilenumbrüchen) vergessen, selbst wenn Sie dies tun Portabilität ist mir egal.
quelle
ls
oderdir
für bestimmte Entwicklertypen ziemlich hoch, genau wiebash
odercmd
oderksh
oder welche Shell Sie bevorzugen.ls
ist höher als das, da es kein direkter Aufruf der Kernel-API Ihres Betriebssystems ist. Es ist eine (kleine) Anwendung.ls
oderdir
aberopendir()/readdir()
(Linux API) oderFindFirstFile()/FindNextFile()
(Windows API) oderFile.listFiles
(Java API) oderDirectory.GetFiles()
(C #). All dies ist eng mit einem direkten Aufruf an das Betriebssystem verbunden. Einige können so einfach sein, wie eine Nummer in ein Register zu schieben und aufzurufenint 13h
, um den Kernelmodus auszulösen.Es ist sicherer. Um Ihnen hier eine Idee zu geben, finden Sie ein Beispielskript
Wenn die Eingabe des Benutzers
test; rm -rf ~
dies wäre, würde das Home-Verzeichnis gelöscht.Aus diesem Grund ist es sicherer, die integrierte Funktion zu verwenden.
Daher sollten Sie auch den Unterprozess anstelle des Systems verwenden.
quelle
Es gibt vier starke Fälle, in denen Pythons spezifischere Methoden im
os
Modul gegenüber der Verwendungos.system
oder demsubprocess
Modul bei der Ausführung eines Befehls bevorzugt werden :os
Modul sind auf mehreren Plattformen verfügbar, während viele Shell-Befehle os-spezifisch sind.os
Modul vermieden werden .Redundanz (siehe redundanten Code ):
Sie führen tatsächlich einen redundanten "Middle-Man" auf dem Weg zu den eventuellen Systemaufrufen aus (
chmod
in Ihrem Beispiel). Dieser mittlere Mann ist ein neuer Prozess oder eine neue Unterschale.Von
os.system
:Und
subprocess
ist nur ein Modul, um neue Prozesse hervorzubringen.Sie können tun, was Sie brauchen, ohne diese Prozesse zu erzeugen.
Portabilität (siehe Quellcode-Portabilität ):
Das
os
Ziel des Moduls ist die Bereitstellung allgemeiner Betriebssystemdienste. Die Beschreibung beginnt mit:Sie können
os.listdir
sowohl unter Windows als auch unter Unix verwenden. Wenn Sie versuchen,os.system
/subprocess
für diese Funktionalität zu verwenden, müssen Sie zwei Aufrufe (fürls
/dir
) beibehalten und überprüfen, auf welchem Betriebssystem Sie sich befinden. Dies ist nicht so portabel und wird später noch mehr Frustration verursachen (siehe Umgang mit der Ausgabe ).Grundlegendes zu den Ergebnissen des Befehls:
Angenommen, Sie möchten die Dateien in einem Verzeichnis auflisten.
Wenn Sie
os.system("ls")
/ verwendensubprocess.call(['ls'])
, können Sie nur die Ausgabe des Prozesses zurückerhalten. Dies ist im Grunde eine große Zeichenfolge mit den Dateinamen.Wie können Sie eine Datei mit einem Leerzeichen im Namen von zwei Dateien unterscheiden?
Was ist, wenn Sie keine Berechtigung zum Auflisten der Dateien haben?
Wie sollten Sie die Daten Python-Objekten zuordnen?
Diese sind nur aus dem Kopf, und obwohl es Lösungen für diese Probleme gibt - warum sollte ich ein Problem, das für Sie gelöst wurde, erneut lösen?
Dies ist ein Beispiel für die folgenden nicht selbst wiederholen Prinzip (oft als „trocken“ reffered to) von nicht eine Implementierung zu wiederholen , die bereits vorhanden und ist für Sie frei zur Verfügung.
Sicherheit:
os.system
undsubprocess
sind mächtig. Es ist gut, wenn Sie diese Kraft brauchen, aber es ist gefährlich, wenn Sie es nicht tun. Wenn Sie verwendenos.listdir
, wissen Sie, dass es nichts anderes tun kann, als Dateien aufzulisten oder einen Fehler auszulösen. Wenn Sieos.system
oder verwendensubprocess
dasselbe Verhalten erreichen, können Sie möglicherweise etwas tun, was Sie nicht wollten.Einspritzsicherheit (siehe Beispiele für die Einspritzung der Schale ) :
Wenn Sie die Eingabe des Benutzers als neuen Befehl verwenden, haben Sie ihm im Grunde eine Shell gegeben. Dies ähnelt der SQL-Injection, die dem Benutzer eine Shell in der Datenbank bereitstellt.
Ein Beispiel wäre ein Befehl des Formulars:
Dies kann leicht ausgeführt werden ausgebeutet jeden beliebigen Code mit der Eingabe:
NASTY COMMAND;#
die schließliche zu erstellen:Es gibt viele solcher Befehle, die Ihr System gefährden können.
quelle
Aus einem einfachen Grund: Wenn Sie eine Shell-Funktion aufrufen, wird eine Sub-Shell erstellt, die nach dem Vorhandensein Ihres Befehls zerstört wird. Wenn Sie also das Verzeichnis in einer Shell ändern, hat dies keine Auswirkungen auf Ihre Umgebung in Python.
Außerdem ist das Erstellen einer Sub-Shell zeitaufwändig, sodass sich die direkte Verwendung von Betriebssystembefehlen auf Ihre Leistung auswirkt
BEARBEITEN
Ich hatte einige Timing-Tests:
Die interne Funktion läuft mehr als zehnmal schneller
EDIT2
Es kann Fälle geben, in denen das Aufrufen einer externen ausführbaren Datei zu besseren Ergebnissen führt als Python-Pakete. Ich habe mich gerade an eine E-Mail erinnert, die von einem meiner Kollegen gesendet wurde, dass die Leistung von gzip, das über einen Unterprozess aufgerufen wurde, viel höher war als die Leistung eines von ihm verwendeten Python-Pakets. Aber sicherlich nicht, wenn es sich um Standard-Betriebssystempakete handelt, die Standard-Betriebssystembefehle emulieren
quelle
%
dem normalen Interpreter.time <path to script>
Terminal eingeben, um die tatsächliche, Benutzer- und Prozesszeit anzuzeigen. Dies ist der Fall, wenn Sie nicht über iPython verfügen und Zugriff auf die Unix-Befehlszeile haben.Shell-Aufrufe sind betriebssystemspezifisch, während Python-OS-Modulfunktionen dies in den meisten Fällen nicht sind. Und es wird vermieden, einen Unterprozess zu erzeugen.
quelle
Es ist weitaus effizienter. Die "Shell" ist nur eine weitere Betriebssystem-Binärdatei, die viele Systemaufrufe enthält. Warum muss der gesamte Shell-Prozess nur für diesen einzelnen Systemaufruf erstellt werden?
Die Situation ist noch schlimmer, wenn Sie
os.system
für etwas verwenden, das keine eingebaute Shell ist. Sie starten einen Shell-Prozess, der wiederum eine ausführbare Datei startet, die dann (zwei Prozesse entfernt) den Systemaufruf ausführt. Zumindestsubprocess
hätte die Notwendigkeit eines Shell-Zwischenprozesses beseitigt.Dies ist nicht spezifisch für Python.
systemd
ist eine solche Verbesserung der Linux-Startzeiten aus dem gleichen Grund: Es führt die erforderlichen Systemaufrufe selbst aus, anstatt tausend Shells zu erzeugen.quelle