Jak uruchomić procesy zewnętrzne z Pythonem i modułem podprocesowym
- 4398
- 1051
- Natan Cholewa
W naszych skryptach automatyzacji często musimy uruchomić i monitorować programy zewnętrzne, aby wykonać nasze pożądane zadania. Podczas pracy z Pythonem możemy użyć modułu podprocesowego do wykonywania wspomnianych operacji. Ten moduł jest częścią standardowej biblioteki języka programowania. W tym samouczku szybko na to przyjrzymy się i poznamy podstawy jego użycia.
W tym samouczku się nauczysz:
- Jak używać funkcji „uruchom” do odrodzenia procesu zewnętrznego
- Jak uchwycić standardowe wyjście i błąd standardowy
- Jak sprawdzić status istnienia procesu i podnieść wyjątek, jeśli się nie powiedzie
- Jak wykonać proces w skorupce pośredniej
- Jak ustawić limit czasu na proces
- Jak używać klasy Popen bezpośrednio do rilania dwóch procesów
Zastosowane wymagania i konwencje oprogramowania
Kategoria | Wymagania, konwencje lub wersja oprogramowania |
---|---|
System | Niezależny dystrybucja |
Oprogramowanie | Python3 |
Inny | Znajomość programowania Pythona i obiektowego |
Konwencje | # - Wymaga, aby podane Linux -commands były wykonywane z uprawnieniami root bezpośrednio jako użytkownik root lub za pomocą sudo Komenda$-wymaga wykonania Linux-commands jako zwykłego niewidzianego użytkownika |
Funkcja „uruchom”
uruchomić funkcja została dodana do podproces moduł tylko w stosunkowo niedawnych wersjach Pythona (3.5). Używanie go jest teraz zalecanym sposobem na odrodzenie procesów i powinien obejmować najczęstsze przypadki użycia. Przed wszystkim innym zobaczmy jego najprostsze użycie. Załóżmy, że chcemy uruchomić LS -al
Komenda; W skorupce Pythona ucieklibyśmy:
>>> Importuj podproces >>> proces = podproces.Run ([„ls”, „-l”, „-a”])
Wyjście polecenia zewnętrznego jest wyświetlane na ekranie:
Łącznie 132 DRWX------. 22 EGDOC EGDOC 4096 30 listopada 12:18 . drwxr-xr-x. 4 Root Root 4096 22 listopada 13: 11… -Rw-------. 1 EGDOC EGDOC 10438 grudnia 1 12:54 .bash_history -Rw-r-r--. 1 EGDOC EGDOC 18 lipca 27 15:10 .bash_logout […]
Właśnie tutaj użyliśmy pierwszego, obowiązkowego argumentu zaakceptowanego przez funkcję, która może być sekwencją, która „opisuje” polecenie i jego argumenty (jak w przykładzie) lub ciąg, którego należy używać podczas działania z Shell = True
Argument (zobaczymy to później).
Uchwycenie polecenia stdout i stderr
Co jeśli nie chcemy, aby dane wyjściowe procesu były wyświetlane na ekranie, ale zamiast tego przechwycone, aby można było się odwołać po wyjściu z procesu? W takim przypadku możemy ustawić capture_output
argument funkcji PRAWDA
:
>>> proces = podproces.RUN ([„ls”, '-l', '-a'], capture_output = true)
Jak możemy później odzyskać wyjście (stdout i stderr) procesu? Jeśli zaobserwujesz powyższe przykłady, możesz zobaczyć, jak użyliśmy proces
zmienna, aby odwołać się do tego, co zwraca uruchomić
Funkcja: Ukończony proces
obiekt. Ten obiekt reprezentuje proces, który został uruchomiony przez funkcję i ma wiele przydatnych właściwości. Między innymi, Stdout
I Stderr
są używane do „przechowywania” odpowiednich deskryptorów polecenia, jeśli, jak powiedzieliśmy, capture_output
argument jest ustawiony na PRAWDA
. W takim przypadku, aby uzyskać Stdout
z procesu, który byśmy uruchomili:
>>> proces.Stdout
Stdout i stderr są przechowywane jako Sekwencje bajtów domyślnie. Jeśli chcemy, aby były przechowywane jako struny, musimy ustawić tekst
argument uruchomić
funkcja PRAWDA
.
Zarządzaj awarią procesu
Polecenie, które przeprowadziliśmy w poprzednich przykładach, zostało wykonane bez błędów. Jednak podczas pisania programu należy wziąć pod uwagę wszystkie sprawy, więc co, jeśli proces zrodzony się nie powiedzie? Domyślnie nic nie wydarzyło się nic „specjalnego”. Zobaczmy przykład; Prowadzimy LS
polecenie ponownie, próbując wymienić treść /źródło
Katalog, który zwykle w Linux nie jest czytelny przez normalnych użytkowników:
>>> proces = podproces.RUN ([„ls”, '-l', '-a', '/root'])
Jedną rzeczą, którą możemy zrobić, aby sprawdzić, czy uruchomiono proces, jest sprawdzenie jego statusu istnienia, który jest przechowywany w kod powrotu
własność Ukończony proces
obiekt:
>>> proces.ReturnCode 2
Widzieć? W tym przypadku kod powrotu był 2
, potwierdzenie, że proces napotkał problem zezwolenia i nie został pomyślnie zakończony. Moglibyśmy przetestować wyjście procesu w ten sposób, a bardziej elegancko, abyśmy mogli zrobić, aby wyjątek został podniesiony, gdy nastąpi porażka. Wejdz do sprawdzać
argument uruchomić
funkcja: gdy jest ustawiona na PRAWDA
i odrodzony proces się nie powiedzie, Zwany ProcesSerror
Wyjątek jest podniesiony:
>>> proces = podproces.run (['ls', '-l', '-a', '/root'], check = true) ls: nie można otworzyć katalogu '/root': uprawnienie odmówione traceback (najnowsze połączenie ostatnie): plik "" , wiersz 1, w pliku "/usr/lib64/python3.9/podproces.Py ", wiersz 524, w runach o nazwieProcessSerror (kod retcode, proces.Args, podproces.Zwany ProcesSerror: Command '[' ls ',' -l ',' -a ','/root '] „zwrócił status wyjścia niezerowego 2.
Obsługiwanie wyjątki W Python jest dość łatwe, więc aby zarządzać awarią procesu, moglibyśmy napisać coś w stylu:
>>> spróbuj:… proces = podproces.RUN ([„ls”, '-l', '-a', '/root'], check = true)… z wyjątkiem podprocesów.Nazywany ProcesSerror jako e:… # tylko przykład, coś przydatnego do zarządzania awarią należy zrobić!… Drukuj (f "e.Cmd nie powiodło się!")… LS: Nie można otworzyć katalogu '/root': uprawnienie odmówione [„ ls ',' -l ',' -a ','/root '] nie powiodło się! >>>
Zwany ProcesSerror
Wyjątek, jak powiedzieliśmy, jest podniesiony, gdy proces wychodzi z non 0
status. Obiekt ma właściwości takie jak kod powrotu
, CMD
, Stdout
, Stderr
; To, co reprezentują, jest dość oczywiste. Na przykład w powyższym przykładzie właśnie użyliśmy CMD
właściwość, w celu zgłoszenia sekwencji używanej do opisania polecenia i jej argumentów w wiadomości, którą napisaliśmy, gdy wystąpił wyjątek.
Wykonaj proces w skorupce
Procesy uruchomione z uruchomić
funkcja, są wykonywane „bezpośrednio”, oznacza to, że do ich uruchomienia nie jest używana: żadne zmienne środowiskowe nie są dostępne dla procesu, a rozszerzenia powłoki nie są wykonywane. Zobaczmy przykład, który obejmuje użycie $ Dom
zmienny:
>>> proces = podproces.RUN ([„ls”, „-al”, „$ home”]) LS: nie można uzyskać dostępu do „$ home”: brak takiego pliku lub katalogu
Jak widać $ Dom
Zmienna nie została rozszerzona. Zaleca się wykonywanie procesów w ten sposób, aby uniknąć potencjalnych zagrożeń bezpieczeństwa. Jeśli jednak w niektórych przypadkach musimy wywołać powłokę jako proces pośredni, musimy ustawić powłoka
parametr uruchomić
funkcja PRAWDA
. W takich przypadkach lepsze jest określenie, że polecenie ma zostać wykonane i jego argumenty jako strunowy:
>>> proces = podproces.RUN („ls -al $ home”, shell = true) ogółem 136 drwx------. 23 EGDOC EGDOC 4096 grudnia 3 09:35 . drwxr-xr-x. 4 Root Root 4096 22 listopada 13: 11… -Rw-------. 1 EGDOC EGDOC 11885 2 grudnia 09:35 .bash_history -Rw-r-r--. 1 EGDOC EGDOC 18 lipca 27 15:10 .bash_logout […]
Wszystkie zmienne istniejące w środowisku użytkownika mogą być używane podczas wywoływania powłoki jako procesu pośredniego: chociaż może to wyglądać przydatne, może być źródłem problemów, szczególnie w przypadku potencjalnie niebezpiecznego wkładu, co może prowadzić zastrzyki skorupowe. Prowadzenie procesu z Shell = True
jest zatem zniechęcony i powinien być używany tylko w bezpiecznych przypadkach.
Określanie limitu czasu na proces
Zwykle nie chcemy, aby procesy źle zachowywane działały wiecznie w naszym systemie po ich uruchomieniu. Jeśli używamy koniec czasu
parametr uruchomić
funkcja, możemy określić czas w sekund. Jeśli nie zostanie ukończony w tym czasie, proces zostanie zabity Sigkill sygnał, którego, jak wiemy, nie może być złapany przez proces. Pokazajmy to, odradzając długotrwały proces i zapewniając limit czasu w kilka sekund:
>>> proces = podproces.Uruchom ([„ping”, „Google.com '], limit czasu = 5) ping Google.com (216.58.206.46) 56 (84) bajty danych. 64 bajtów z MIL07S07-in-F14.1E100.netto (216.58.206.46): ICMP_SEQ = 1 TTL = 113 Czas = 29.3 ms 64 bajtów z LHR35S10-in-F14.1E100.netto (216.58.206.46): ICMP_SEQ = 2 TTL = 113 Time = 28.3 ms 64 bajtów z LHR35S10-in-F14.1E100.netto (216.58.206.46): ICMP_SEQ = 3 ttl = 113 czas = 28.5 ms 64 bajtów z LHR35S10-in-F14.1E100.netto (216.58.206.46): ICMP_SEQ = 4 ttl = 113 czas = 28.5 ms 64 bajtów z LHR35S10-in-F14.1E100.netto (216.58.206.46): ICMP_SEQ = 5 ttl = 113 czas = 28.1 MS Traceback (najnowsze połączenie ostatnie): Plik „”, wiersz 1, w pliku ”/usr/lib64/python3.9/podproces.py ", linia 503, w run stdout, stderr = proces.komunikat (wejście, limit czasu = limit czasu) plik "/usr/lib64/python3.9/podproces.py ", wiersz 1130, w komunikacji stdout, stderr = self._Communiate (Input, Endtime, Timeout) plik ”/usr/lib64/pyhon3.9/podproces.Py ", wiersz 2003, w _komunice self.Poczekaj (limit czasu = jaźń._REMINING_TIME (endTime)) plik "/usr/lib64/python3.9/podproces.py ", wiersz 1185, w oczekiwaniu._Wait (TIMEOUT = TIMEOUT) PLIK ”/USR/Lib64/Python3.9/podproces.Py ", wiersz 1907, w _ -Wait hode hode timeoutexpired (self self.Args, limit czasu) podproces.TimeOutexpired: Command '[„ping”, „Google.com '] „wyczerpano po 4.999826977029443 sekundy
W powyższym przykładzie uruchomiliśmy świst
polecenie bez określania stałej ilości Żądanie ECHO Pakiety, dlatego potencjalnie mogą działać wiecznie. Określiliśmy również limit czasu 5
sekundy za pośrednictwem koniec czasu
parametr. Jak możemy zaobserwować początkowo program, ale Limit czasu upłynął
Wyjątek został podniesiony, gdy osiągnięto określoną ilość sekund, a proces został zabity.
Funkcje wywołania, check_output i check_call
Jak powiedzieliśmy wcześniej, uruchomić
Funkcja jest zalecanym sposobem uruchomienia procesu zewnętrznego i powinna pokryć większość przypadków. Zanim został wprowadzony w Python 3.5, trzy główne funkcje API wysokiego poziomu używane do uruchomienia procesu były dzwonić
, check_output
I Check_Call
; Zobaczmy je krótko.
Po pierwsze, dzwonić
funkcja: służy do uruchamiania polecenia opisanego przez Args
parametr; czeka na zakończenie polecenia i zwraca jego kod powrotu. Z grubsza odpowiada podstawowe użycie uruchomić
funkcjonować.
Check_Call
Zachowanie funkcji jest praktycznie takie samo w stosunku do zachowania uruchomić
funkcja, gdy sprawdzać
parametr jest ustawiony na PRAWDA
: uruchamia określone polecenie i czeka na to. Jeśli jego istnienie nie jest 0
, A Zwany ProcesSerror
Wyjątek jest podniesiony.
Wreszcie check_output
Funkcja: Działa podobnie jak Check_Call
, Ale zwroty Wyjście programu: nie jest wyświetlane po wykonywaniu funkcji.
Praca na niższym poziomie z klasą Popen
Do tej pory badaliśmy funkcje API wysokiego poziomu w module podprocesów, szczególnie uruchomić
. Wszystkie to funkcje, pod maską wchodzą w interakcje z Popen
klasa. Z tego powodu w zdecydowanej większości przypadków nie musimy z tym pracować bezpośrednio. Kiedy jednak potrzebna jest większa elastyczność, tworzenie Popen
obiekty bezpośrednio stają się konieczne.
Załóżmy, że na przykład chcemy podłączyć dwa procesy, odtwarzając zachowanie „rury” skorupy. Jak wiemy, kiedy wbijamy dwa polecenia w skorupce, standardowe wyjście po lewej stronie rury (|
) jest używany jako standardowe wejście tego po prawej stronie (sprawdź ten artykuł na temat przekierowań powłoki, jeśli chcesz dowiedzieć się więcej na ten temat). W poniższym przykładzie wynik rurowania dwa polecenia jest przechowywane w zmiennej:
$ output = "$ (dmesg | grep sda)"
Naśladowanie tego zachowania za pomocą modułu podprocesowego, bez konieczności ustawiania powłoka
parametr do PRAWDA
Jak widzieliśmy wcześniej, musimy użyć Popen
klasa bezpośrednio:
dmesg = podproces.Popen (['dmesg'], stdout = podproces.Rura) grep = podproces.Popen ([„grep”, „sda”], stdin = dmesg.stdout) dmesg.Stdout.close () wyjście = grep.comunecate () [0]
Aby zrozumieć powyższy przykład, musimy pamiętać, że proces rozpoczął się od użycia Popen
klasa bezpośrednio nie blokuje wykonywania skryptu, ponieważ teraz jest on czeka.
Pierwszą rzeczą, którą zrobiliśmy w powyższym fragmencie kodu, było stworzenie Popen
obiekt reprezentujący Dmesg proces. Ustawiliśmy Stdout
tego procesu podproces.RURA
: Ta wartość wskazuje, że należy otworzyć rurę do określonego strumienia.
Nie stworzyliśmy kolejnej instancji Popen
klasa dla Grep proces. w Popen
Konstruktor określiliśmy oczywiście polecenie i jego argumenty, ale oto ważna część, ustawiamy standardowe wyjście Dmesg proces, który należy zastosować jako standardowe wejście (stdin = dmesg.Stdout
), aby odtworzyć skorupę
Zachowanie rur.
Po utworzeniu Popen
obiekt dla Grep polecenie, zamknęliśmy Stdout
strumień Dmesg proces za pomocą zamknąć()
Metoda: To, jak stwierdzono w dokumentacji, jest potrzebne, aby umożliwić pierwszemu procesowi odbieranie sygnału SIGPIPE. Spróbujmy wyjaśnić, dlaczego. Zwykle, gdy dwa procesy są połączone rurą, jeśli jedna po prawej stronie rury (GREP w naszym przykładzie) wychodzi przed jedną po lewej (DMESG), ten ostatni otrzymuje Sigpipe
sygnał (zepsuta rura) i domyślnie się kończy.
Podczas replikacji zachowania rury między dwoma poleceniami w Pythonie istnieje problem: Stdout pierwszego procesu jest otwarty zarówno w skrypcie nadrzędnym, jak i w standardowym wejściu drugiego procesu. W ten sposób, nawet jeśli Grep Proces kończy się, rura nadal pozostanie otwarta w procesie dzwoniącego (nasz skrypt), dlatego pierwszy proces nigdy nie otrzyma Sigpipe sygnał. Dlatego musimy zamknąć Stdout strumienia pierwszego procesu w naszym
główny skrypt po uruchomieniu drugiego.
Ostatnią rzeczą, którą zrobiliśmy, było zadzwonienie komunikować się()
Metoda na Grep obiekt. Tę metodę można zastosować do opcjonalnego przekazywania danych wejściowych do procesu; czeka na zakończenie procesu i zwraca krotkę, w którym proces jest procesem pierwszego członka Stdout (do którego odwołuje się wyjście
zmienna), a drugi proces Stderr.
Wnioski
W tym samouczku widzieliśmy zalecany sposób odrwienia procesów zewnętrznych za pomocą Pythona za pomocą podproces moduł i uruchomić
funkcjonować. Zastosowanie tej funkcji powinno wystarczyć dla większości przypadków; Gdy jednak potrzebny jest wyższy poziom elastyczności, należy użyć Popen
klasa bezpośrednio. Jak zawsze sugerujemy spojrzeć na
Dokumentacja podprocesowa dla pełnego przeglądu podpisu funkcji i klas dostępnych w
moduł.
Powiązane samouczki Linux:
- Wprowadzenie do automatyzacji, narzędzi i technik Linuksa
- Mastering Bash Script Loops
- Rzeczy do zainstalowania na Ubuntu 20.04
- Zagnieżdżone pętle w skryptach Bash
- Jak używać polecenia TCPDUMP w Linux
- Mint 20: Lepsze niż Ubuntu i Microsoft Windows?
- Obsługa danych wejściowych użytkownika w skryptach Bash
- Samouczek debugowania GDB dla początkujących
- Rzeczy do zrobienia po zainstalowaniu Ubuntu 20.04 Focal Fossa Linux
- Jak monitorować aktywność sieciową w systemie Linux
- « Jak utworzyć pakiet RPM
- Jak utworzyć konto modyfikowania i usuwania użytkowników w systemie Linux »