Die anderen Antworten hier erklären angemessen die Sicherheitsvorkehrungen, die auch in der subprocess
-Dokumentation erwähnt sind. Aber zusätzlich dazu ist der Overhead beim Starten einer Shell, um das Programm zu starten, das Sie ausführen möchten, oft unnötig und definitiv albern für Situationen, in denen Sie tatsächlich keine der Shell-Funktionen verwenden. Darüber hinaus sollte die zusätzliche versteckte Komplexität Sie abschrecken, insbesondere, wenn Sie mit der Shell oder den von ihr bereitgestellten Diensten nicht sehr vertraut sind.
Wenn die Interaktionen mit der Shell nicht unerheblich sind, erfordern Sie jetzt, dass der Leser und der Wartende des Python-Skripts (der möglicherweise nicht Ihr zukünftiges Ich ist) sowohl Python als auch Shell-Skript verstehen. Denken Sie an das Python-Motto "explizit ist besser als implizit"; selbst wenn der Python-Code etwas komplexer ist als das äquivalente (und oft sehr knappe) Shell-Skript, könnte es besser sein, die Shell zu entfernen und die Funktionalität durch native Python-Konstrukte zu ersetzen. Die Minimierung der Arbeit in einem externen Prozess und die Aufrechterhaltung der Kontrolle innerhalb Ihres eigenen Codes soweit möglich ist oft eine gute Idee, einfach weil sie die Sichtbarkeit verbessert und die Risiken von -- erwünschten oder unerwünschten -- Nebenwirkungen reduziert.
Wildcards, Variableninterpolation und Umleitungen sind alle einfach durch native Python-Konstrukte zu ersetzen. Eine komplexe Shell-Pipeline, die teilweise oder ganz vernünftigerweise nicht in Python neu geschrieben werden kann, wäre die eine Situation, in der Sie vielleicht die Shell verwenden könnten. Sie sollten jedoch sicherstellen, dass Sie die Leistungs- und Sicherheitsauswirkungen verstehen.
In einfachen Fällen, um shell=True
zu vermeiden, ersetzen Sie einfach
subprocess.Popen("Befehl -mit -Optionen 'so' und\\ ein\\ Argument", shell=True)
durch
subprocess.Popen(['Befehl', '-mit', '-Optionen', 'so', 'und ein Argument'])
Beachten Sie, wie das erste Argument eine Liste von Zeichenfolgen ist, die an execvp()
übergeben werden sollen, und wie das Anführungszeichen um Zeichenfolgen und das Backslash-Escapen von Shell-Metazeichen im Allgemeinen nicht erforderlich (oder nützlich oder korrekt) ist. Vielleicht sehen Sie auch Wann man Anführungszeichen um eine Shell-Variable setzen sollte?
Wenn Sie dies nicht selbst herausfinden möchten, kann die Funktion shlex.split()
dies für Sie tun. Es ist Teil der Python-Standardbibliothek, aber natürlich können Sie, wenn Ihre Shell-Befehlszeichenfolge statisch ist, sie einfach einmal während der Entwicklung ausführen und das Ergebnis in Ihr Skript einfügen.
Als Anmerkung, Sie möchten sehr oft Popen
vermeiden, wenn eine der einfacheren Wrapper im subprocess
-Paket das tut, was Sie möchten. Wenn Sie ein aktuelles Python haben, sollten Sie wahrscheinlich subprocess.run
verwenden.
- Mit
check=True
schlägt es fehl, wenn der von Ihnen ausgeführte Befehl fehlschlägt.
- Mit
stdout=subprocess.PIPE
wird die Ausgabe des Befehls erfasst.
- Mit
text=True
(oder etwas obskurer, mit dem Synonym universal_newlines=True
) wird die Ausgabe in eine ordnungsgemäße Unicode-Zeichenfolge decodiert (ansonsten sind es nur bytes
im Systemencoding, auf Python 3).
Wenn nicht, für viele Aufgaben möchten Sie check_output
verwenden, um die Ausgabe eines Befehls zu erhalten, während Sie überprüfen, ob er erfolgreich war, oder check_call
, wenn es keine Ausgabe zu erfassen gibt.
Ich schließe mit einem Zitat von David Korn: "Es ist einfacher, eine portable Shell zu schreiben als ein portables Shell-Skript." Selbst subprocess.run('echo "$HOME"', shell=True)
ist nicht auf Windows portierbar.
47 Stimmen
Der erste Befehl ist falsch:
-l
wird an/bin/sh
(die Shell) übergeben statt an dasls
Programm auf Unix wennshell=True
. Ein String-Argument sollte in den meisten Fällen mitshell=True
verwendet werden anstelle einer Liste.3 Stimmen
Bitte übersetzen: "der Prozess wird direkt gestartet": Was?
21 Stimmen
Die Aussage "Beide funktionieren." über diese 2 Aufrufe ist falsch und irreführend. Die Aufrufe funktionieren unterschiedlich. Das Umschalten von
shell=True
aufFalse
und umgekehrt ist ein Fehler. Aus Dokumenten: "Auf POSIX mit shell=True, (...) Wenn args eine Sequenz ist, gibt das erste Element den Befehlsstring an, und alle zusätzlichen Elemente werden als zusätzliche Argumente für die Shell selbst behandelt.". Auf Windows gibt es eine automatische Umwandlung, die unerwünscht sein könnte.0 Stimmen
Siehe auch stackoverflow.com/q/59641747/874188
2 Stimmen
@DeusXMachina Du wiederholst fälschlicherweise die beiden älteren Kommentare, die dies erklären.
subprocess.run(['ls', '-l'r, shell=True)
führt dazu, dasssh -c 'ls' 'sh' '-l'
ausgeführt wird. Die Argumente werden nicht "stillschweigend ignoriert", sondern man muss wissen, wie man damit umgeht. Zugegeben, für die meisten praktischen Zwecke lautet die einfachste und meist korrekte Anleitung: "Verwenden Sieshell=True
nicht, wenn Sie eine Liste von Token übergeben, und umgekehrt". Windows toleriert dies besser, ist jedoch aus anderen Gründen natürlich völlig untragbar.