Rzeczywiste znaczenie 'shell = True' w podprocesie

Wywołuję różne procesy za pomocą modułu subprocess. Mam jednak pytanie.

W następujących kodach:

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

I

callProcess = subprocess.Popen(['ls', '-l']) # without shell
Oba działają. Po przeczytaniu dokumentów dowiedziałem się, że shell=True oznacza wykonywanie kodu przez powłokę. Oznacza to, że w przypadku braku Proces jest bezpośrednio uruchamiany.

Więc co powinienem preferować dla mojego przypadku-muszę uruchomić proces i uzyskać jego wynik. Jakie korzyści mam z wywołania go z wewnątrz shell lub poza nim.

Author: rypel, 2010-07-03

6 answers

Zaletą nie wywołania przez powłokę jest to, że nie wywołujesz ' tajemniczego programu.'W POSIX zmienna środowiskowa SHELL kontroluje, który plik binarny jest wywoływany jako powłoka"."W Windows nie ma powłoki bourne' a, tylko cmd.exe.

Wywołanie powłoki wywołuje program wybrany przez użytkownika i jest zależne od platformy. Ogólnie rzecz biorąc, unikaj wywoływania przez powłokę.

Wywołanie przez powłokę pozwala na rozszerzenie zmiennych środowiskowych i Pliku globusy według zwyczajowego mechanizmu powłoki. W systemach POSIX powłoka rozszerza Globus plików do listy plików. W systemie Windows glob plików (np."*.* ") i tak nie jest rozwijana przez powłokę (ale Zmienne środowiskowe w wierszu poleceń są rozwijane przez cmd.exe).

Jeśli uważasz, że chcesz rozszerzeń zmiennych środowiskowych i Globów plików, zbadaj ataki ILS z 1992 roku na usługi sieciowe, które wykonywały podprogram wywołań za pośrednictwem powłoki. Przykłady obejmują różne sendmail backdoory z udziałem ILS.

Podsumowując, użyj shell=False.

 207
Author: Heath Hunnicutt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-03 18:46:48
>>> import subprocess
>>> subprocess.call('echo $HOME')
Traceback (most recent call last):
...
OSError: [Errno 2] No such file or directory
>>>
>>> subprocess.call('echo $HOME', shell=True)
/user/khong
0

Ustawienie argumentu powłoki na wartość true powoduje, że podproces wywołuje pośredni proces powłoki i każe mu uruchomić polecenie. Innymi słowy, użycie powłoki pośredniej oznacza, że zmienne, wzorce glob i inne specjalne funkcje powłoki w łańcuchu poleceń są przetwarzane przed uruchomieniem polecenia. Tutaj, w przykładzie, $HOME został przetworzony przed poleceniem echo. W rzeczywistości jest to przypadek polecenia z rozszerzeniem powłoki, podczas gdy polecenie ls-l uważane jest za proste polecenie.

Source: Subprocess Module

 162
Author: Mina Gabriel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-30 04:05:35

Przykład, gdzie rzeczy mogą pójść nie tak z Shell = True jest pokazany tutaj

>>> from subprocess import call
>>> filename = input("What file would you like to display?\n")
What file would you like to display?
non_existent; rm -rf / # THIS WILL DELETE EVERYTHING IN ROOT PARTITION!!!
>>> call("cat " + filename, shell=True) # Uh-oh. This will end badly...

Sprawdź doc tutaj: podproces.call()

 45
Author: Richeek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-13 01:36:25

Wykonywanie programów przez powłokę oznacza, że wszystkie dane wejściowe użytkownika przekazywane do programu są interpretowane zgodnie ze składnią i regułami semantycznymi wywołanej powłoki. W najlepszym wypadku powoduje to tylko niedogodności dla użytkownika, ponieważ użytkownik musi przestrzegać tych zasad. Na przykład, ścieżki zawierające specjalne znaki powłoki, takie jak cudzysłowy lub spacje, muszą być unikalne. W najgorszym wypadku powoduje przecieki bezpieczeństwa, ponieważ użytkownik może wykonywać dowolne programy.

shell=True jest czasami wygodne korzystanie z określonych funkcji powłoki, takich jak dzielenie słów lub rozszerzanie parametrów. Jeśli jednak taka funkcja jest wymagana, skorzystaj z innych modułów (np. {[1] } dla rozszerzenia parametrów lub shlex dla podziału słów). Oznacza to więcej pracy, ale pozwala uniknąć innych problemów.

W skrócie: unikaj shell=True za wszelką cenę.

 40
Author: lunaryorn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-25 18:10:20

Pozostałe odpowiedzi tutaj odpowiednio wyjaśniają zastrzeżenia dotyczące bezpieczeństwa, które są również wymienione w dokumentacji subprocess. Ale oprócz tego, obciążenie powłoką, aby uruchomić program, który chcesz uruchomić, jest często niepotrzebne i zdecydowanie głupie w sytuacjach, w których nie używasz żadnej z funkcjonalności powłoki. Co więcej, dodatkowa ukryta złożoność powinna cię przestraszyć, szczególnie jeśli nie jesteś zbyt zaznajomiony z powłoką lub usługami, które ona oferuje zapewnia.

Tam, gdzie interakcje z powłoką są nietrywialne, teraz wymagasz od czytelnika i opiekuna skryptu Pythona (który może, ale nie musi być twoim przyszłym ja) zrozumienia zarówno Pythona, jak i skryptu powłoki. Pamiętaj motto Pythona "explicit is better than implicit"; nawet jeśli kod Pythona będzie nieco bardziej złożony niż odpowiedni (i często bardzo zwięzły) skrypt powłoki, może być lepiej usunąć powłokę i zastąpić funkcjonalność z natywnymi konstrukcjami Pythona. Minimalizacja pracy wykonanej w zewnętrznym procesie i utrzymanie kontroli w ramach własnego kodu jest często dobrym pomysłem, ponieważ poprawia widoczność i zmniejsza ryzyko-pożądanych lub niechcianych-skutków ubocznych.

Rozszerzanie symboli wieloznacznych, interpolacja zmiennych i przekierowanie są proste do zastąpienia natywnymi konstrukcjami Pythona. Złożonym rurociągiem powłoki, w którym części lub wszystkie nie mogą być przepisane w Pythonie, byłby ten sytuacja, w której mógłbyś rozważyć użycie powłoki. Nadal należy upewnić się, że rozumiesz wpływ wydajności i bezpieczeństwa.

W błahym przypadku, aby uniknąć shell=True, wystarczy zastąpić

subprocess.Popen("command -with -options 'like this' and\\ an\\ argument", shell=True)

Z

subprocess.Popen(['command', '-with','-options', 'like this', 'and an argument'])

Zauważ, że pierwszy argument jest listą łańcuchów, które mają zostać przekazane execvp(), oraz że cytowanie łańcuchów i metacharakterów powłoki z odwrotnym ukośnikiem nie jest na ogół konieczne (lub użyteczne lub poprawne). Może Zobacz także kiedy owijać cudzysłowy wokół powłoki zmienna?

Jeśli nie chcesz tego rozgryźć samodzielnie, Funkcja shlex.split() może to zrobić za Ciebie. Jest to część standardowej biblioteki Pythona, ale oczywiście, jeśli ciąg poleceń powłoki jest statyczny, możesz go uruchomić tylko raz, podczas tworzenia i wkleić wynik do skryptu.

Na marginesie, bardzo często chcesz uniknąć Popen, jeśli jedna z prostszych opakowań w opakowaniu subprocess zrobi to, co chcesz. Jeśli masz wystarczająco niedawno Pythona, prawdopodobnie powinieneś użyć subprocess.run.

  • Z check=True nie powiedzie się, jeśli uruchomione polecenie nie powiedzie się.
  • Z stdout=subprocess.PIPE przechwyci wyjście polecenia.
  • z text=True (lub nieco niejasne, z synonimem universal_newlines=True) dekoduje wyjście do odpowiedniego ciągu znaków Unicode(jest to po prostu bytes w kodowaniu systemowym, inaczej, w Pythonie 3).

Jeśli nie, do wielu zadań, chcesz check_output aby uzyskać wyjście z polecenia, sprawdzając, czy się powiodło, lub check_call jeśli nie ma wyjścia do zebrania.

[17]}na zakończenie przytoczę cytat Davida Korna: "łatwiej jest napisać przenośną powłokę niż przenośny skrypt powłoki."Nawet {[16] } nie jest przenośny Dla Windows.
 20
Author: tripleee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-10-15 08:42:12

Załóżmy, że używasz shell = False I podajesz polecenie jako listę. A jakiś złośliwy użytkownik próbował wstrzyknąć komendę "rm". Zobaczysz, że 'rm' będzie interpretowane jako argument i efektywnie' ls 'spróbuje znaleźć plik o nazwie'rm'

>>> subprocess.run(['ls','-ld','/home','rm','/etc/passwd'])
ls: rm: No such file or directory
-rw-r--r--    1 root     root          1172 May 28  2020 /etc/passwd
drwxr-xr-x    2 root     root          4096 May 29  2020 /home
CompletedProcess(args=['ls', '-ld', '/home', 'rm', '/etc/passwd'], returncode=1)

Shell = False nie jest domyślnie zabezpieczony, jeśli nie kontrolujesz poprawnie wejścia. Nadal możesz wykonywać niebezpieczne polecenia.

>>> subprocess.run(['rm','-rf','/home'])
CompletedProcess(args=['rm', '-rf', '/home'], returncode=0)
>>> subprocess.run(['ls','-ld','/home'])
ls: /home: No such file or directory
CompletedProcess(args=['ls', '-ld', '/home'], returncode=1)
>>>

Piszę większość moich aplikacji w środowiskach kontenerów, wiem, która powłoka jest wywoływane i nie biorę żadnego wkładu użytkownika.

Więc w moim przypadku nie widzę zagrożenia bezpieczeństwa. I znacznie łatwiej jest tworzyć długie ciągi poleceń. Mam nadzieję, że się nie mylę.
 -1
Author: lauc.exon.nod,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-10 22:28:55