Po co używać metod modułu os w Pythonie zamiast bezpośrednio wykonywać polecenia powłoki?

157

Próbuję zrozumieć, jaka jest motywacja stojąca za używaniem funkcji bibliotecznych Pythona do wykonywania zadań specyficznych dla systemu operacyjnego, takich jak tworzenie plików / katalogów, zmiana atrybutów plików itp., Zamiast po prostu wykonywać te polecenia za pomocą os.system()lub subprocess.call()?

Na przykład, dlaczego miałbym chcieć używać os.chmodzamiast robić os.system("chmod...")?

Rozumiem, że bardziej „pythonowe” jest używanie dostępnych metod bibliotecznych Pythona tak często, jak to możliwe, zamiast tylko bezpośredniego wykonywania poleceń powłoki. Ale czy jest jakaś inna motywacja do zrobienia tego z punktu widzenia funkcjonalności?

Mówię tutaj tylko o wykonywaniu prostych jednowierszowych poleceń powłoki. Kiedy potrzebujemy większej kontroli nad wykonaniem zadania, rozumiem, że np. Użycie subprocessmodułu ma większy sens.

Koderok
źródło
6
Zasadniczo uderzyłeś w gwóźdź w głowę. Zadania na poziomie systemu operacyjnego, do których się odnosisz, są na tyle powszechne, że zapewniały swoją własną funkcję, a nie tylko były wywoływane przez os.system.
deweyredman
7
Przy okazji, czy próbowałeś mierzyć czas wykonania - os.chmod vs. os.system ("chmod ...") . Zaryzykowałbym przypuszczenie, że odpowie na część twojego pytania.
wulkan
61
Dlaczego printkiedy mogłeś os.system("echo Hello world!")?
user253751
25
Z tego samego powodu powinieneś używać os.pathdo obsługi ścieżek zamiast obsługiwać je ręcznie: działa w każdym systemie operacyjnym, na którym działa.
Bakuriu,
51
„Bezpośrednie wykonywanie poleceń powłoki” jest w rzeczywistości mniej bezpośrednie. Powłoka nie jest niskopoziomowym interfejsem systemu i os.chmodnie będzie wywoływać chmodprogramu, który by zrobił. Korzystanie os.system('chmod ...')uruchamia powłokę interpretować ciąg, aby zadzwonić do innego pliku wykonywalnego, aby wykonać połączenie do C chmodfunkcja, gdy os.chmod(...)idzie o wiele bardziej bezpośrednio do folderu C chmod.
user2357112 obsługuje Monikę

Odpowiedzi:

325
  1. Jest szybciej , os.systemi subprocess.calltworzyć nowe procesy, które jest konieczne do czegoś to proste. W rzeczywistości, os.systemi subprocess.callz shellargumentacji zwykle utworzyć co najmniej dwa nowe procesy: pierwszy z nich to powłoki, a drugi jest polecenie, które używasz (jeśli nie jest to powłoka wbudowane w podobny test).

  2. Niektóre polecenia są bezużyteczne w oddzielnym procesie . Na przykład, jeśli uruchomisz os.spawn("cd dir/"), zmieni bieżący katalog roboczy procesu potomnego, ale nie procesu Pythona. Musisz do tego użyć os.chdir.

  3. Nie musisz martwić się o znaki specjalne interpretowane przez powłokę. os.chmod(path, mode)będzie działać bez względu na nazwę pliku, ale os.spawn("chmod 777 " + path)zakończy się strasznym niepowodzeniem, jeśli nazwa pliku będzie podobna ; rm -rf ~. (Pamiętaj, że możesz to obejść, jeśli używasz subprocess.callbez shellargumentu).

  4. Nie musisz się martwić o nazwy plików zaczynające się od myślnika . os.chmod("--quiet", mode)zmieni uprawnienia pliku o nazwie --quiet, ale os.spawn("chmod 777 --quiet")zakończy się niepowodzeniem, co --quietjest interpretowane jako argument. Dotyczy to nawet subprocess.call(["chmod", "777", "--quiet"]).

  5. Masz mniej problemów między platformami i powłokami, ponieważ standardowa biblioteka Pythona powinna sobie z tym poradzić. Czy Twój system ma chmodpolecenia? Czy jest zainstalowany? Czy obsługuje parametry, które ma obsługiwać? osModuł będzie starał się być jak cross-platform, jak to możliwe i dokumentów, gdy to nie jest możliwe.

  6. Jeśli uruchamiane polecenie ma wyjście, na którym Ci zależy, musisz je przeanalizować, co jest trudniejsze niż się wydaje, ponieważ możesz zapomnieć o przypadkach narożnych (nazwach plików ze spacjami, tabulatorami i znakami nowej linii), nawet jeśli nie obchodzi mnie przenośność.

Flimm
źródło
38
Aby dodać do punktu "międzyplatformowego", lista katalogu to "ls" w systemie Linux, "dir" w systemie Windows. Pobieranie zawartości katalogu jest bardzo powszechnym zadaniem niskiego poziomu.
Cort Ammon
1
@CortAmmon: „Low-Level” jest względne, lsalbo dirsą dość wysoki poziom niektórych rodzajów programistów, jak bashlub cmdlub kshlub cokolwiek zapłacić wolisz są.
Sebastian Mach,
1
@phresnel: Nigdy nie myślałem o tym w ten sposób. Dla mnie „bezpośrednie wywołanie API jądra twojego systemu operacyjnego” było bardzo niskim poziomem. Zakładam, że umyka mi inna perspektywa, ponieważ (naturalnie) podchodzę do tego z własnymi uprzedzeniami.
Cort Ammon
5
@CortAmmon: racja, i lsjest na wyższym poziomie, ponieważ nie jest to bezpośrednie wywołanie API jądra twojego systemu operacyjnego. To (mała) aplikacja.
Steve Jessop
1
@SteveJessop. Nazywałem "pobieranie zawartości katalogu" niskim poziomem. Nie myślę lsczy dirjednak opendir()/readdir()(Linux API) lub FindFirstFile()/FindNextFile()(Windows API) lub File.listFiles(Java API) lub Directory.GetFiles()(C #). Wszystko to jest ściśle powiązane z bezpośrednim wywołaniem systemu operacyjnego. Niektóre mogą być tak proste, jak wstawienie liczby do rejestru i wywołanie int 13htrybu jądra.
Cort Ammon,
133

To jest bezpieczniejsze. Aby dać ci pomysł, oto przykładowy skrypt

import os
file = raw_input("Please enter a file: ")
os.system("chmod 777 " + file)

Jeśli dane wejściowe użytkownika były test; rm -rf ~takie, spowoduje to usunięcie katalogu domowego.

Dlatego bezpieczniej jest korzystać z funkcji wbudowanej.

Dlatego też powinieneś używać podprocesu zamiast systemu.

iProgram
źródło
26
Albo inaczej spojrzeć na to, co jest łatwiejsze do zrobienia, pisząc programy w Pythonie lub pisząc programy w Pythonie, które piszą skrypty powłoki? :-)
Steve Jessop
3
@SteveJessop, mój kolega, był zdumiony, że mały skrypt w Pythonie, który pomogłem mu napisać, działał 20 (!) Razy szybciej niż skrypt powłoki. Wyjaśniłem, że przekierowanie wyjścia może wyglądać seksownie - ale pociąga za sobą otwieranie i zamykanie pliku w każdej iteracji. Ale niektórzy uwielbiają robić rzeczy na
wulkan
1
@SteveJessop, to jest podchwytliwe pytanie - nie wiedziałbyś, aż do uruchomienia! :)
60

Istnieją cztery mocne argumenty przemawiające za preferowaniem bardziej specyficznych metod Pythona w osmodule zamiast używania os.systemlub subprocessmodułu podczas wykonywania polecenia:

  • Nadmiarowość - tworzenie kolejnego procesu jest zbędne i powoduje marnowanie czasu i zasobów.
  • Przenośność - wiele metod osmodułu jest dostępnych na wielu platformach, podczas gdy wiele poleceń powłoki jest specyficznych dla systemu operacyjnego.
  • Zrozumienie wyników - tworzenie procesu wykonującego dowolne polecenia zmusza do przeanalizowania wyników z danych wyjściowych i zrozumienia, czy i dlaczego polecenie spowodowało coś złego.
  • Bezpieczeństwo - proces może potencjalnie wykonać każde wydane polecenie. Jest to słaby projekt i można go uniknąć, stosując określone metody w osmodule.

Nadmiarowość (patrz kod nadmiarowy ):

W rzeczywistości wykonujesz zbędnego „pośrednika” w drodze do ewentualnych wywołań systemowych ( chmodw twoim przykładzie). Ten pośrednik jest nowym procesem lub podpowłoką.

Od os.system:

Wykonaj polecenie (ciąg) w podpowłoce ...

I subprocessjest tylko moduł do odradzania nowych procesów.

Możesz robić to, czego potrzebujesz, bez tworzenia tych procesów.

Przenośność (patrz przenośność kodu źródłowego ):

Celem osmodułu jest zapewnienie ogólnych usług systemu operacyjnego, a jego opis zaczyna się od:

Ten moduł zapewnia przenośny sposób korzystania z funkcji zależnych od systemu operacyjnego.

Możesz używać os.listdirzarówno w systemie Windows, jak i unix. Próba użycia os.system/ subprocessdo tej funkcji zmusi Cię do utrzymania dwóch wywołań (dla ls/ dir) i sprawdzenia, w jakim systemie operacyjnym jesteś. To nie jest tak przenośny i będzie powodować jeszcze większą frustrację później (patrz Magazynowanie Output ).

Zrozumienie wyników polecenia:

Załóżmy, że chcesz wyświetlić listę plików w katalogu.

Jeśli używasz os.system("ls")/ subprocess.call(['ls']), możesz odzyskać tylko dane wyjściowe procesu, które są w zasadzie dużym ciągiem z nazwami plików.

Jak odróżnić plik ze spacją w nazwie od dwóch plików?

A jeśli nie masz uprawnień do wyświetlania plików?

Jak należy odwzorować dane na obiekty Pythona?

Są to tylko z góry mojej głowy, i chociaż istnieją rozwiązania tych problemów - po co rozwiązywać ponownie problem, który został rozwiązany za Ciebie?

To jest przykład przestrzegania zasady „ Nie powtarzaj się ” (często określanej jako „SUCHA”) poprzez nie powtarzanie implementacji, która już istnieje i jest dostępna bezpłatnie.

Bezpieczeństwo:

os.systemi subprocesssą potężne. Dobrze, gdy potrzebujesz tej mocy, ale jest to niebezpieczne, gdy jej nie potrzebujesz. Kiedy używasz os.listdir, wiesz, że nie może zrobić nic innego niż wyświetlenie plików lub zgłoszenie błędu. Kiedy używasz os.systemlub subprocessosiągasz to samo zachowanie, możesz potencjalnie zrobić coś, czego nie chciałeś zrobić.

Bezpieczeństwo wtrysku (patrz przykłady wtrysku powłoki ) :

Jeśli używasz danych wejściowych od użytkownika jako nowego polecenia, w zasadzie dałeś mu powłokę. Jest to podobne do iniekcji SQL zapewniającej użytkownikowi powłokę w bazie danych.

Przykładem może być polecenie w postaci:

# ... read some user input
os.system(user_input + " some continutation")

To może być łatwo wykorzystany do uruchomienia dowolnego dowolnego kodu przy użyciu wkład: NASTY COMMAND;#stworzyć ostateczne:

os.system("NASTY COMMAND; # some continuation")

Istnieje wiele takich poleceń, które mogą stanowić zagrożenie dla systemu.

Reut Sharabani
źródło
3
Powiedziałbym, że 2. jest głównym powodem.
jaredad7
23

Z prostego powodu - kiedy wywołujesz funkcję powłoki, tworzy ona podpowłokę, która jest niszczona po zaistnieniu twojego polecenia, więc jeśli zmienisz katalog w powłoce - nie wpływa to na twoje środowisko w Pythonie.

Poza tym tworzenie podpowłoki jest czasochłonne, więc bezpośrednie użycie poleceń systemu operacyjnego wpłynie na wydajność

EDYTOWAĆ

Przeprowadziłem kilka testów czasowych:

In [379]: %timeit os.chmod('Documents/recipes.txt', 0755)
10000 loops, best of 3: 215 us per loop

In [380]: %timeit os.system('chmod 0755 Documents/recipes.txt')
100 loops, best of 3: 2.47 ms per loop

In [382]: %timeit call(['chmod', '0755', 'Documents/recipes.txt'])
100 loops, best of 3: 2.93 ms per loop

Funkcja wewnętrzna działa ponad 10 razy szybciej

EDYCJA2

Mogą zdarzyć się sytuacje, w których wywołanie zewnętrznego pliku wykonywalnego może dać lepsze wyniki niż pakiety Pythona - właśnie przypomniałem sobie wiadomość wysłaną przez mojego kolegę, że wydajność gzipa wywołanego przez podproces była znacznie wyższa niż wydajność pakietu Pythona, którego używał. Ale na pewno nie, gdy mówimy o standardowych pakietach systemu operacyjnego emulujących standardowe polecenia systemu operacyjnego

wulkan
źródło
Czy jest to przypadkiem zrobione z iPythonem? Nie sądziłem, że możesz użyć specjalnych funkcji, zaczynając od %używania zwykłego interpretera.
iProgram
@aPyDeveloper, tak, to był iPython - na Ubuntu. "Magiczny" % czas to błogosławieństwo - chociaż są pewne przypadki - głównie z formatowaniem ciągów - których nie może przetworzyć
wulkan
1
Możesz też utworzyć skrypt w języku Python, a następnie wpisać time <path to script> terminal, który poinformuje Cię o czasie rzeczywistym, użytkowniku i procesie. To znaczy, jeśli nie masz iPythona i masz dostęp do wiersza poleceń systemu Unix.
iProgram
1
@aPyDeveloper, nie widzę powodu do ciężkiej pracy - kiedy mam iPythona na moim komputerze
volcano
Prawdziwe! Powiedziałem, że jeśli nie masz iPythona. :)
iProgram
16

Wywołanie powłoki jest specyficzne dla systemu operacyjnego, podczas gdy funkcje modułu os w Pythonie nie są w większości przypadków. I pozwala uniknąć tworzenia podprocesu.

JoshRomRock
źródło
1
Funkcje modułów Pythona również tworzą nowe podprocesy w celu wywołania nowej podpowłoki.
Koderok
7
@Koderok bzdury, funkcje modułu są nazywane w trakcie
dwurf
3
@Koderok: moduł os używa podstawowych wywołań systemowych używanych przez polecenie powłoki, nie używa poleceń powłoki. Oznacza to, że wywołanie systemowe OS jest zwykle bezpieczniejsze i szybsze (bez analizowania łańcuchów, boo fork, bez exec, zamiast tego jest to tylko wywołanie jądra) niż polecenia powłoki. Zauważ, że w większości przypadków wywołanie powłoki i wywołanie systemowe często mają podobną lub taką samą nazwę, ale są one udokumentowane oddzielnie; wywołanie powłoki znajduje się w sekcji man 1 (domyślna sekcja man), podczas gdy równoważnie nazwane wywołanie systemowe znajduje się w sekcji man 2 (np. man 2 chmod).
Lie Ryan
1
@ dwurf, LieRyan: Mój błąd! Wygląda na to, że miałem złe pojęcie. Dzięki!
Koderok
11

Jest dużo bardziej wydajne. „Powłoka” to po prostu kolejny plik binarny systemu operacyjnego, który zawiera wiele wywołań systemowych. Po co ponosić koszty związane z tworzeniem całego procesu powłoki tylko dla tego pojedynczego wywołania systemowego?

Sytuacja jest jeszcze gorsza, gdy używasz os.systemczegoś, co nie jest wbudowaną powłoką. Uruchamiasz proces powłoki, który z kolei uruchamia plik wykonywalny, który następnie (dwa procesy dalej) wykonuje wywołanie systemowe. Przynajmniej subprocesswyeliminowałoby potrzebę pośrednictwa powłoki.

To nie jest specyficzne dla Pythona. systemdjest takim ulepszeniem czasów uruchamiania Linuksa z tego samego powodu: sam wykonuje niezbędne wywołania systemowe zamiast tworzyć tysiące powłok.

MSalters
źródło