423 Stimmen

Tatsächliche Bedeutung von 'shell=True' in subprocess

Ich rufe verschiedene Prozesse mit dem subprocess Modul auf. Allerdings habe ich eine Frage.

In dem folgenden Code:

callProcess = subprocess.Popen(['ls', '-l'], shell=True)

und

callProcess = subprocess.Popen(['ls', '-l']) # ohne shell

Beide funktionieren. Nachdem ich die Dokumentation gelesen habe, habe ich herausgefunden, dass shell=True bedeutet, den Code über die Shell auszuführen. Das heißt, in Abwesenheit wird der Prozess direkt gestartet.

Was sollte ich also für meinen Fall bevorzugen - ich muss einen Prozess ausführen und seine Ausgabe erhalten. Welchen Vorteil habe ich davon, ihn innerhalb oder außerhalb der Shell aufzurufen?

47 Stimmen

Der erste Befehl ist falsch: -l wird an /bin/sh (die Shell) übergeben statt an das ls Programm auf Unix wenn shell=True. Ein String-Argument sollte in den meisten Fällen mit shell=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 auf False 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.

2voto

demonguy Punkte 1699

Die obige Antwort erklärt es richtig, aber nicht direkt genug. Lass uns den ps Befehl verwenden, um zu sehen was passiert.

import time
import subprocess

s = subprocess.Popen(["sleep 100"], shell=True)
print("start")
print(s.pid)
time.sleep(5)
s.kill()
print("finish")

Führe es aus und zeige

start
832758
finish

Dann kannst du ps -auxf > 1 vor finish und dann ps -auxf > 2 nach finish verwenden. Hier ist die Ausgabe

1

cy         71209  0.0  0.0   9184  4580 pts/6    Ss   Oct20   0:00  |       \_ /bin/bash
cy        832757  0.2  0.0  13324  9600 pts/6    S+   19:31   0:00  |       |   \_ python /home/cy/Desktop/test.py
cy        832758  0.0  0.0   2616   612 pts/6    S+   19:31   0:00  |       |       \_ /bin/sh -c sleep 100
cy        832759  0.0  0.0   5448   532 pts/6    S+   19:31   0:00  |       |           \_ sleep 100

Siehst du? Anstatt direkt sleep 100 auszuführen, führt es tatsächlich /bin/sh aus. und die pid, die es ausgibt, ist tatsächlich die pid von /bin/sh. Wenn du dann s.kill() aufrufst, tötet es /bin/sh, aber sleep ist immer noch vorhanden.

2

cy         69369  0.0  0.0 533764  8160 ?        Ssl  Oct20   0:12  \_ /usr/libexec/xdg-desktop-portal
cy         69411  0.0  0.0 491652 14856 ?        Ssl  Oct20   0:04  \_ /usr/libexec/xdg-desktop-portal-gtk
cy        832646  0.0  0.0   5448   596 pts/6    S    19:30   0:00  \_ sleep 100

Die nächste Frage ist, was kann /bin/sh tun? Jeder Linux-Benutzer kennt es, hat von ihm gehört und verwendet ihn. Aber ich wette, dass es viele Menschen gibt, die eigentlich nicht verstehen, was shell tatsächlich ist. Vielleicht hast du auch schon von /bin/bash gehört, sie sind ähnlich.

Eine offensichtliche Funktion der Shell ist es, Benutzern die Bequemlichkeit zu bieten, Linux-Anwendungen auszuführen. Durch Shell-Programme wie sh oder bash kannst du beispielsweise den Befehl ls direkt verwenden, anstatt /usr/bin/ls. Es wird dann suchen, wo ls ist und es für dich ausführen.

Die andere Funktion besteht darin, dass sie Zeichenfolgen nach dem $ als Umgebungsvariable interpretiert. Du kannst diese beiden Python-Skripte vergleichen, um es selbst herauszufinden.

subprocess.call(["echo $PATH"], shell=True)

subprocess.call(["echo", "$PATH"])

Und das Wichtigste: Es ermöglicht das Ausführen von Linux-Befehlen als Skript. Solche wie if else werden von der Shell eingeführt. Es sind keine native Linux-Befehle

-3voto

lauc.exon.nod Punkte 105

Angenommen, Sie verwenden shell=False und geben den Befehl als Liste an. Und ein bösartiger Benutzer versucht, einen 'rm'-Befehl einzufügen. Sie werden sehen, dass 'rm' als Argument interpretiert wird und effektiv 'ls' versuchen wird, eine Datei namens 'rm' zu finden.

>> subprocess.run(['ls', '-ld', '/home', 'rm', '/etc/passwd'])
ls: rm: Datei oder Verzeichnis nicht gefunden
-rw-r--r--    1 root     root          1172 28. Mai 2020 /etc/passwd
drwxr-xr-x    2 root     root          4096 29. Mai 2020 /home
CompletedProcess(args=['ls', '-ld', '/home', 'rm', '/etc/passwd'], returncode=1)

shell=False ist standardmäßig nicht sicher, wenn Sie die Eingabe nicht richtig kontrollieren. Sie können immer noch gefährliche Befehle ausführen.

>> subprocess.run(['rm', '-rf', '/home'])
CompletedProcess(args=['rm', '-rf', '/home'], returncode=0)
>>> subprocess.run(['ls', '-ld', '/home'])
ls: /home: Datei oder Verzeichnis nicht gefunden
CompletedProcess(args=['ls', '-ld', '/home'], returncode=1)
>>>

Ich schreibe die meisten meiner Anwendungen in Container-Umgebungen, ich weiß, welcher Shell aufgerufen wird und ich nehme keine Benutzereingaben entgegen.

Also in meinem Anwendungsfall sehe ich kein Sicherheitsrisiko. Und es ist viel einfacher, lange Zeichenketten von Befehlen zu erstellen. Hoffentlich liege ich nicht falsch.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X