Pytanie Wywoływanie zewnętrznego polecenia w Pythonie


Jak mogę wywołać zewnętrzne polecenie (tak, jakbym wpisał je w powłoce Unix lub wierszu poleceń systemu Windows) z poziomu skryptu Python?


3635
2017-09-18 01:35


pochodzenie




Odpowiedzi:


Spójrz na moduł podprocesowy w standardowej bibliotece:

from subprocess import call
call(["ls", "-l"])

Zaleta podproces vs. system jest bardziej elastyczny (możesz uzyskać standardowe wyjście, stderr, "prawdziwy" kod statusu, lepszą obsługę błędów itd.).

The oficjalna dokumentacja poleca podproces moduł nad alternatywną os.system ():

The podproces moduł zapewnia mocniejsze możliwości tworzenia nowych procesów i odzyskiwania ich wyników; użycie tego modułu jest lepsze niż użycie tej funkcji [os.system()].

"Zastępując starsze funkcje modułem podprocesowym"sekcja w podproces dokumentacja może mieć kilka pomocnych przepisów.

Oficjalna dokumentacja na temat podproces moduł:


3504
2017-09-18 01:39



Czy istnieje sposób na zastosowanie substytucji zmiennych? IE próbowałem zrobić echo $PATH używając call(["echo", "$PATH"]), ale po prostu powtórzył ciąg literowy $PATH zamiast dokonywać jakichkolwiek zmian. Wiem, że mogę uzyskać zmienną środowiskową PATH, ale zastanawiam się, czy istnieje prosty sposób, aby polecenie zachowywało się dokładnie tak, jakbym wykonał go w bashu. - Kevin Wheeler
@ KevinWheeler Będziesz musiał użyć shell=True aby to działało. - SethMMorton
@ KevinWheeler NIE powinieneś używać shell=True, w tym celu pojawia się Python os.path.expandvars. W twoim przypadku możesz napisać: os.path.expandvars("$PATH"). @SethMMorton, proszę ponownie rozważyć swój komentarz -> Dlaczego nie używać powłoki = prawda - Murmel
Od wersji Python 3.5 sugeruje się, że używasz subprocess.run zamiast subprocess.call. docs.python.org/3/library/subprocess.html - Hannes Karppila
Przykładowe wywołania ls -l ale nie daje dostępu do swoich wyników (stdout nie jest dostępne). Uważam, że to mylące - można zamiast tego użyć polecenia bez standardowego, na przykład touch. - florisla


Oto podsumowanie sposobów wywoływania programów zewnętrznych oraz ich zalety i wady:

  1. os.system("some_command with args") przekazuje polecenie i argumenty do powłoki twojego systemu. Jest to miłe, ponieważ w ten sposób można uruchamiać jednocześnie wiele poleceń i konfigurować potoki oraz przekierowanie wejścia / wyjścia. Na przykład:

    os.system("some_command < input_file | another_command > output_file")  
    

    Jednak, chociaż jest to wygodne, musisz ręcznie obsługiwać znaki powłoki, takie jak spacje itp. Z drugiej strony, pozwala to również uruchamiać polecenia, które są po prostu komendami powłoki, a nie programami zewnętrznymi. Widzieć dokumentacja.

  2. stream = os.popen("some_command with args") zrobi to samo, co os.system oprócz tego, że daje obiekt podobny do pliku, który można wykorzystać do uzyskania dostępu do standardowego wejścia / wyjścia dla tego procesu. Istnieją 3 inne warianty popen, które wszystkie obsługują i / o nieco inaczej. Jeśli przekazujesz wszystko jako ciąg, twoje polecenie jest przekazywane do powłoki; jeśli przekażesz je jako listę, nie musisz się martwić o ucieczkę. Widzieć dokumentacja.

  3. The Popen klasa subprocess moduł. Ma to zastąpić os.popen ale ma tę wadę, że jest nieco bardziej skomplikowany ze względu na jego wszechstronność. Na przykład powiesz:

    print subprocess.Popen("echo Hello World", shell=True, stdout=subprocess.PIPE).stdout.read()
    

    zamiast:

    print os.popen("echo Hello World").read()
    

    ale dobrze jest mieć wszystkie opcje w jednej zunifikowanej klasie, zamiast w 4 różnych funkcjach popen. Widzieć dokumentacja.

  4. The call funkcja z subprocess moduł. Zasadniczo jest to podobne do Popen class i przyjmuje wszystkie te same argumenty, ale po prostu czeka, aż polecenie się zakończy i poda kod powrotu. Na przykład:

    return_code = subprocess.call("echo Hello World", shell=True)  
    

    Widzieć dokumentacja.

  5. Jeśli korzystasz z Python 3.5 lub nowszego, możesz użyć nowego subprocess.run funkcja, która jest bardzo podobna do powyższej, ale jeszcze bardziej elastyczna i zwraca a CompletedProcess obiekt po wykonaniu polecenia.

  6. Moduł os posiada również wszystkie funkcje fork / exec / spawn, które posiadasz w programie C, ale nie polecam ich używać bezpośrednio.

The subprocess moduł powinien prawdopodobnie być tym, czego używasz.

Na koniec pamiętaj, że dla wszystkich metod, które przekazują ostateczną komendę do wykonania przez powłokę jako ciąg i jesteś odpowiedzialny za jej ucieczkę. Istnieją poważne konsekwencje dla bezpieczeństwa jeśli jakakolwiek część przekazywanego ciągu nie może być w pełni zaufana. Na przykład, jeśli użytkownik wprowadza część / jakąkolwiek część ciągu. Jeśli nie masz pewności, używaj tylko tych metod ze stałymi. Aby dać ci wskazówkę dotyczącą implikacji, rozważ ten kod:

print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()

i wyobraź sobie, że użytkownik wchodzi "moja mama nie kochała mnie && rm -rf /".


2469
2017-09-18 13:11



Ładna odpowiedź / wyjaśnienie. W jaki sposób ta odpowiedź uzasadnia motto Pythona opisane w tym artykule? fastcompany.com/3026446/...  "Stylistycznie, Perl i Python mają różne filozofie, a najlepiej znane motto Perla" Istnieje więcej niż jeden sposób, aby to zrobić "Python zaprojektowano tak, by miał jeden oczywisty sposób na zrobienie tego" Wygląda na to, że powinno być inaczej! W Perlu znam tylko dwa sposoby wykonania polecenia - za pomocą back-tick lub open. - Jean
Jeśli korzystasz z Pythona 3.5+, użyj subprocess.run(). docs.python.org/3.5/library/subprocess.html#subprocess.run - phoenix
To, co zwykle należy wiedzieć, to to, co robi się ze STDOUT i STDERR procesu potomnego, ponieważ jeśli są one ignorowane, w pewnych (dość powszechnych) warunkach, ostatecznie proces potomny będzie wywoływał systemowe wywołanie zapisu do STDOUT (STDERR też?) to przekroczyłoby bufor wyjściowy dostarczony dla systemu przez system operacyjny, a system operacyjny spowoduje zablokowanie go, dopóki jakiś proces nie odczyta z tego bufora. Tak więc przy obecnie zalecanych sposobach subprocess.run(..), co dokładnie robi "Domyślnie nie przechwytuje stdout ani stderr." sugerować? Co powiesz na subprocess.check_output(..) i STDERR? - Evgeni Sergeev
które polecane przez ciebie polecenia blokują mój skrypt? to znaczy, jeśli chcę uruchomić wiele poleceń w for pętla jak to zrobić bez blokowania mojego skryptu Pythona? Nie dbam o wynik polecenia, po prostu chcę je uruchomić. - Charlie Parker
@phoenix Nie zgadzam się. Nic nie stoi na przeszkodzie, aby użyć os.system w python3 docs.python.org/3/library/os.html#os.system - Qback


Zwykle używam:

import subprocess

p = subprocess.Popen('ls', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in p.stdout.readlines():
    print line,
retval = p.wait()

Możesz robić, co chcesz, dzięki stdout dane w rurze. W rzeczywistości można po prostu pominąć te parametry (stdout= i stderr=) i będzie się zachowywać jak os.system().


257
2017-09-18 18:20



.readlines() czyta wszystko linie na raz, to znaczy, blokuje się, aż podproces kończy działanie (zamyka swój koniec rury). Aby czytać w czasie rzeczywistym (jeśli nie ma problemów z buforowaniem), możesz: for line in iter(p.stdout.readline, ''): print line, - jfs
Czy możesz wyjaśnić, co masz na myśli przez "czy nie ma problemów z buforowaniem"? Jeśli proces blokuje się zdecydowanie, wywołanie podprocesu również blokuje się. To samo mogłoby się zdarzyć z moim oryginalnym przykładem. Co jeszcze może się wydarzyć w odniesieniu do buforowania? - EmmEff
proces potomny może używać buforowania bloków w trybie nieinteraktywnym zamiast buforowania liniowego p.stdout.readline()(Uwaga: nie s na końcu) nie zobaczy żadnych danych, dopóki dziecko nie wypełni swojego bufora. Jeśli dziecko nie generuje dużej ilości danych, dane wyjściowe nie będą wyświetlane w czasie rzeczywistym. Zobacz drugi powód w P: Dlaczego po prostu nie użyć rury (popen ())?. Zapewniono niektóre obejścia w tej odpowiedzi (pexpect, pty, stdbuf) - jfs
problem z buforowaniem ma znaczenie tylko wtedy, gdy chcesz wyświetlać dane w czasie rzeczywistym i nie dotyczy kodu, który nie drukuje niczego, dopóki wszystko dane są odbierane - jfs


Kilka wskazówek na temat odłączania procesu potomnego od wywołującego (rozpoczęcie procesu potomnego w tle).

Załóżmy, że chcesz uruchomić długie zadanie ze skryptu CGI, to znaczy proces potomny powinien żyć dłużej niż proces wykonywania skryptu CGI.

Klasyczny przykład z dokumentów modułu podprocesu to:

import subprocess
import sys

# some code here

pid = subprocess.Popen([sys.executable, "longtask.py"]) # call subprocess

# some more code here

Pomysł polega na tym, że nie chcesz czekać w linii "wywołaj podproces", dopóki nie zakończy się longtask.py. Ale nie jest jasne, co dzieje się po linii "trochę więcej kodu tutaj" z przykładu.

Moja platforma docelowa była freebsd, ale rozwój był w oknach, więc najpierw stanąłem przed problemem na windows.

W systemie Windows (win xp) proces nadrzędny nie zakończy się, dopóki plik longtask.py nie zakończy pracy. Nie jest to, czego chcesz w skrypcie CGI. Problem nie jest specyficzny dla Pythona, w społeczności PHP problemy są takie same.

Rozwiązanie ma przejść DETACHED_PROCESS Flaga tworzenia procesu do podstawowej funkcji CreateProcess w API win. Jeśli zdarzyło ci się zainstalować pywin32, możesz zaimportować flagę z modułu win32process, w przeciwnym razie powinieneś zdefiniować to samodzielnie:

DETACHED_PROCESS = 0x00000008

pid = subprocess.Popen([sys.executable, "longtask.py"],
                       creationflags=DETACHED_PROCESS).pid

/ * UPD 2015.10.27 @eryksun w komentarzu poniżej zauważa, że ​​znacznik semantycznie poprawny to CREATE_NEW_CONSOLE (0x00000010) * /

Na freebsd mamy inny problem: kiedy proces nadrzędny jest zakończony, kończy on także procesy potomne. I to nie jest to, czego chcesz w skrypcie CGI. Niektóre eksperymenty pokazały, że problem polegał na udostępnianiu sys.stdout. A rozwiązanie robocze było następujące:

pid = subprocess.Popen([sys.executable, "longtask.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)

Nie sprawdzałem kodu na innych platformach i nie znam przyczyn tego zachowania na freebsd. Jeśli ktoś wie, podziel się swoimi pomysłami. Googling na uruchamianie procesów w tle w Pythonie jeszcze nie rzuca światła.


158
2018-02-12 10:15



zauważyłem możliwe "dziwactwo" z rozwijającymi się aplikacjami py2exe w pydev + eclipse. udało mi się stwierdzić, że główny skrypt nie został odłączony, ponieważ okno wyjściowe eclipse nie kończyło się; nawet jeśli skrypt wykonuje się do końca, to wciąż czeka na zwroty. ale, gdy próbowałem kompilacji do pliku wykonywalnego py2exe, oczekiwane zachowanie występuje (uruchamia procesy jako odłączone, a następnie kończy działanie). nie jestem pewien, ale nazwa pliku wykonywalnego nie znajduje się już na liście procesów. działa to dla wszystkich podejść (os.system ("start *"), os.spawnl z os.P_DETACH, podprocesy itp.) - maranas
Windows gotcha: mimo że uruchomiłem proces z DETACHED_PROCESS, kiedy zabiłem mojego demona Pythona, wszystkie otwarte przez niego porty nie uwolnią się, dopóki nie zakończą się wszystkie procesy odradzania. WScript.Shell rozwiązał wszystkie moje problemy. Przykład tutaj: pastebin.com/xGmuvwSx - Alexey Lebedev
może być również potrzebna flaga CREATE_NEW_PROCESS_GROUP. Widzieć Popen czeka na proces potomny, nawet gdy natychmiastowe dziecko się zakończyło - jfs
Następujące informacje są nieprawidłowe: "[o] n Windows (win xp) proces nadrzędny nie zakończy się, dopóki longtask.py nie zakończy pracy". Element nadrzędny zakończy działanie normalnie, ale okno konsoli (instancja conhost.exe) zostanie zamknięte tylko po zakończeniu ostatniego dołączonego procesu, a element potomny może odziedziczyć konsolę nadrzędną. Oprawa DETACHED_PROCESS w creationflags unika się tego, uniemożliwiając dziecku dziedziczenie lub tworzenie konsoli. Jeśli potrzebujesz nowej konsoli, użyj CREATE_NEW_CONSOLE (0x00000010). - eryksun
Nie miałem na myśli tego, że wykonanie jako oddzielnego procesu jest nieprawidłowe. Mimo to możesz potrzebować ustawić standardowe uchwyty dla plików, potoków lub os.devnull ponieważ niektóre programy konsoli wychodzą z błędem w przeciwnym razie. Utwórz nową konsolę, jeśli proces potomny ma współdziałać z użytkownikiem równolegle z procesem nadrzędnym. Byłoby mylące, aby spróbować zrobić to w jednym oknie. - eryksun


Zalecam użycie modułu subprocess zamiast os.system, ponieważ powoduje to ucieczkę powłoki i jest o wiele bezpieczniejszy: http://docs.python.org/library/subprocess.html

subprocess.call(['ping', 'localhost'])

98
2017-09-18 01:42





import os
cmd = 'ls -al'
os.system(cmd)

Jeśli chcesz zwrócić wyniki polecenia, możesz użyć os.popen. Jest to jednak przestarzałe od wersji 2.6 na rzecz moduł podprocesowy, które inne odpowiedzi pokrywają się dobrze.


94
2017-09-18 01:37



popen jest przestarzałe na korzyść podproces. - Fox Wilson
Możesz także zapisać wynik za pomocą wywołania os.system, ponieważ działa on jak powłoka UNIX, jak na przykład os.system ("ls -l> test2.txt") - Stefan Gruenwald


import os
os.system("your command")

Zauważ, że jest to niebezpieczne, ponieważ polecenie nie jest czyszczone. Pozostawiam Tobie Google'a odpowiednią dokumentację dotyczącą modułów "os" i "sys". Istnieje wiele funkcji (exec * i spawn *), które wykonają podobne czynności.


84
2017-09-18 01:37



Co masz na myśli poprzez "polecenie nie jest wyczyszczone"? - Peter Mortensen
Nie mam pojęcia, co miałem na myśli prawie dziesięć lat temu (sprawdź datę!), Ale gdybym musiał zgadywać, byłoby to niemożliwe. - nimish