Wywoływanie zewnętrznego polecenia z Python


Odpowiedzi:


4692

Spójrz na moduł podprocesu w standardowej bibliotece:

import subprocess
subprocess.run(["ls", "-l"])

Zaletą subprocessvs. systemjest to, że jest bardziej elastyczny (można uzyskać stdout, stderr, „prawdziwy” kod statusu, lepsza obsługa błędów, itp ...).

Oficjalna dokumentacja zaleca subprocessmoduł nad alternatywą os.system():

subprocessModuł zapewnia bardziej wydajne urządzenia do tarła nowych procesów i pobierania ich wyników; korzystanie z tego modułu jest lepsze niż używanie tej funkcji [ os.system()].

Do Wymiana Starsze Funkcje z podprocesu Module sekcji w subprocessdokumentacji może mieć kilka pomocnych recepty.

W przypadku wersji Python wcześniejszych niż 3.5 użyj call:

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

Czy istnieje sposób na stosowanie substytucji zmiennych? IE Próbowałem to zrobić echo $PATHza pomocą call(["echo", "$PATH"]), ale po prostu powtórzyło literalny ciąg $PATHzamiast dokonywać podstawienia. Wiem, że mogę pobrać zmienną środowiskową PATH, ale zastanawiam się, czy istnieje prosty sposób, aby polecenie zachowywało się dokładnie tak, jakbym wykonał je w trybie bash.
Kevin Wheeler

@KevinWheeler Musisz użyć shell=True, aby to zadziałało.
SethMMorton

38
@KevinWheeler NIE powinieneś używać shell=True, w tym celu Python jest dostarczany z os.path.expandvars . W twoim przypadku można napisać: os.path.expandvars("$PATH"). @SethMMorton proszę ponownie rozważyć swój komentarz -> Dlaczego nie użyć shell = True
Murmel

blokuje połączenia? tzn. jeśli chcę uruchamiać wiele poleceń w forpętli, jak to zrobić bez blokowania mojego skryptu python? Nie dbam o wynik polecenia, chcę po prostu uruchomić wiele z nich.
Charlie Parker

4
Jeśli chcesz utworzyć listę z polecenia z parametrami , listę, z której można skorzystać, subprocesskiedy shell=False, a następnie użyj w shlex.splitcelu łatwego wykonania tej czynności docs.python.org/2/library/shlex.html#shlex.split
Daniel F

2982

Oto podsumowanie sposobów wywoływania programów zewnętrznych oraz zalety i wady każdego z nich:

  1. os.system("some_command with args")przekazuje polecenie i argumenty do powłoki systemu. Jest to miłe, ponieważ możesz w ten sposób uruchomić wiele poleceń jednocześnie i skonfigurować przekierowania potoków i wejść / wyjść. Na przykład:

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

Jednak, chociaż jest to wygodne, musisz ręcznie obsługiwać zmiany znaczenia znaków powłoki, takich jak spacje itp. Z drugiej strony pozwala to również na uruchamianie poleceń, które są po prostu poleceniami powłoki, a nie programami zewnętrznymi. Zobacz dokumentację .

  1. stream = os.popen("some_command with args")zrobi to samo co os.systempoza tym, że da ci obiekt podobny do pliku, którego możesz użyć do uzyskania dostępu do standardowego wejścia / wyjścia dla tego procesu. Istnieją 3 inne warianty popen, które nieco inaczej obsługują wejścia / wyjścia. Jeśli przekażesz wszystko jako ciąg, twoje polecenie zostanie przekazane do powłoki; jeśli przekażesz je jako listę, nie musisz się martwić, że coś uciekniesz. Zobacz dokumentację .

  2. PopenKlasa subprocessmodułu. Ma to na celu zastąpienie, os.popenale ma tę wadę, że jest nieco bardziej skomplikowane ze względu na to, że jest tak wszechstronne. Na przykład powiedziałbyś:

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

    zamiast:

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

    ale fajnie jest mieć wszystkie opcje w jednej zunifikowanej klasie zamiast 4 różnych popenowych funkcji. Zobacz dokumentację .

  3. callFunkcji z subprocessmodułu. Jest to w zasadzie tak samo jak Popenklasa 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)  

    Zobacz dokumentację .

  4. Jeśli korzystasz z języka Python 3.5 lub nowszego, możesz użyć nowej subprocess.runfunkcji, która jest bardzo podobna do powyższej, ale jeszcze bardziej elastyczna i zwraca CompletedProcessobiekt po zakończeniu wykonywania polecenia.

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

subprocessModuł powinien prawdopodobnie czego używasz.

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

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

i wyobraź sobie, że użytkownik wprowadza coś „moja mama mnie nie kochała && rm -rf /”, co może skasować cały system plików.


22
Dobra 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. Najbardziej znanymi mottami Perla jest„ Jest więcej niż jeden sposób, aby to zrobić ”. Python ma jeden oczywisty sposób na zrobienie tego.„ Wygląda na to, że powinno być inna droga! W Perlu znam tylko dwa sposoby wykonania polecenia - używając back-tick lub open.
Jean

12
Jeśli używasz Python 3.5+, użyj subprocess.run(). docs.python.org/3.5/library/subprocess.html#subprocess.run
feniks

4
Zwykle trzeba wiedzieć, co się dzieje z STDOUT i STDERR procesu potomnego, ponieważ jeśli zostaną zignorowane, w niektórych (dość powszechnych) warunkach, w końcu proces potomny wyda wywołanie systemowe, aby napisać do STDOUT (STDERR?) przekraczałoby to bufor wyjściowy przewidziany dla procesu przez system operacyjny, a system operacyjny spowoduje jego zablokowanie, dopóki jakiś proces nie odczyta z tego bufora. Tak więc, przy obecnie zalecanych sposobach, subprocess.run(..)co dokładnie robi „To domyślnie nie przechwytuje stdout ani stderr”. sugerować? A co z subprocess.check_output(..)STDERR?
Evgeni Sergeev

2
@Pitto tak, ale nie to wykonuje przykład. Zauważ, że echoprzed ciągiem przekazanym do Popen? Tak będzie pełne polecenie echo my mama didnt love me && rm -rf /.
Chris Arndt,

6
Jest to prawdopodobnie niewłaściwy sposób. Większość ludzi potrzebuje tylko subprocess.run()starszego rodzeństwa subprocess.check_call()i in. W przypadkach, w których nie wystarczają, patrz subprocess.Popen(). os.popen()nie powinno być wcale wspomniane, ani nawet przychodzić po „zhakowaniu własnego kodu fork / exec / spawn”.
tripleee

357

Typowa realizacja:

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 dowolnie robić z stdoutdanymi w potoku. W rzeczywistości możesz po prostu pominąć te parametry ( stdout=i stderr=) i będzie się tak zachowywał os.system().


44
.readlines()odczytuje wszystkie wiersze naraz, tzn. blokuje, dopóki podproces nie zakończy działania (nie zamknie końca potoku). 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

1
Czy mógłbyś wyjaśnić, co rozumiesz przez „jeśli nie występują problemy z buforowaniem”? Jeśli proces blokuje się zdecydowanie, wywołanie podprocesu również blokuje. To samo może się zdarzyć z moim oryginalnym przykładem. Co jeszcze może się stać w odniesieniu do buforowania?
EmmEff,

15
proces potomny może używać buforowania bloków w trybie nieinteraktywnym zamiast buforowania linii, więc p.stdout.readline()(uwaga: nie sna końcu) nie zobaczy żadnych danych, dopóki dziecko nie zapełni bufora. Jeśli dziecko nie generuje dużych ilości danych, dane wyjściowe nie będą przesyłane w czasie rzeczywistym. Zobacz drugi powód w pytaniu: Dlaczego po prostu nie użyć potoku (popen ())? . Niektóre obejścia są zawarte w tej odpowiedzi (pexpect, pty, stdbuf)
jfs

4
problem buforowania ma znaczenie tylko wtedy, gdy chcesz uzyskać dane wyjściowe w czasie rzeczywistym i nie dotyczy twojego kodu, który niczego nie drukuje, dopóki wszystkie dane nie zostaną odebrane
jfs

3
Ta odpowiedź była odpowiednia jak na swój czas, ale nie powinniśmy już polecać Popenprostych zadań. To również niepotrzebnie określa shell=True. Wypróbuj jedną z subprocess.run()odpowiedzi.
tripleee

230

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

Załóżmy, że chcesz rozpocząć długie zadanie od skryptu CGI. Oznacza to, że proces potomny powinien żyć dłużej niż proces wykonywania skryptu CGI.

Klasyczny przykład z dokumentacji modułu podprocesu to:

import subprocess
import sys

# Some code here

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

# Some more code here

Chodzi o to, że nie chcesz czekać w wierszu „wywołanie podprocesu”, aż do zakończenia longtask.py. Ale nie jest jasne, co dzieje się po wierszu „trochę więcej kodu tutaj” z przykładu.

Moją platformą docelową był FreeBSD, ale prace rozwojowe dotyczyły systemu Windows, więc najpierw napotkałem ten problem.

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

Rozwiązaniem jest przekazanie flagi tworzenia procesu DETACHED_PROCESS do podstawowej funkcji CreateProcess w interfejsie API systemu Windows. Jeśli akurat masz zainstalowany pywin32, możesz zaimportować flagę z modułu win32process, w przeciwnym razie powinieneś ją zdefiniować 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 ​​semantycznie poprawną flagą jest CREATE_NEW_CONSOLE (0x00000010) * /

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

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

Nie sprawdziłem kodu na innych platformach i nie znam przyczyn takiego zachowania na FreeBSD. Jeśli ktoś wie, podziel się swoimi pomysłami. Googling po uruchomieniu procesów w tle w Pythonie nie rzuca jeszcze światła.


zauważyłem możliwy „dziwactwo” z tworzeniem aplikacji py2exe w pydev + eclipse. mogłem stwierdzić, że główny skrypt nie został odłączony, ponieważ okno wyjściowe zaćmienia nie kończyło się; nawet jeśli skrypt zostanie wykonany do końca, nadal czeka na zwroty. ale kiedy próbowałem kompilować do pliku wykonywalnego py2exe, występuje oczekiwane zachowanie (uruchamia procesy jako odłączone, a następnie kończy działanie). nie jestem pewien, ale nazwy wykonywalnej nie ma już na liście procesów. działa to dla wszystkich podejść (os.system („start *”), os.spawnl z os.P_DETACH, podproki itp.)
maranas

1
możesz także potrzebować flagi CREATE_NEW_PROCESS_GROUP. Zobacz Popen czekający na proces potomny, nawet jeśli bezpośrednie dziecko zakończyło pracę
jfs

5
Niepoprawne: „[o] n Windows (Win XP) proces nadrzędny nie zakończy się, dopóki longtask.py nie zakończy pracy”. Nadrzędny zakończy działanie normalnie, ale okno konsoli (instancja conhost.exe) zamyka się dopiero po zakończeniu ostatniego dołączonego procesu, a dziecko mogło odziedziczyć konsolę nadrzędną. Ustawienie DETACHED_PROCESSw creationflagsunika tego poprzez zapobieganie dziecko od dziedziczenia lub tworząc konsolę. Jeśli zamiast tego chcesz nową konsolę, użyj CREATE_NEW_CONSOLE(0x00000010).
Eryk Sun

1
Nie miałem na myśli tego, że wykonywanie jako odłączony proces jest niepoprawne. To powiedziawszy, może być konieczne ustawienie standardowych uchwytów na pliki, potoki lub os.devnullponieważ niektóre programy konsolowe wychodzą z błędem inaczej. Utwórz nową konsolę, jeśli chcesz, aby proces potomny współdziałał z użytkownikiem jednocześnie z procesem nadrzędnym. Byłoby mylące, aby próbować zrobić oba w jednym oknie.
Eryk Sun

1
czy nie istnieje sposób niezależny od systemu operacyjnego, aby proces działał w tle?
Charlie Parker

151
import os
os.system("your command")

Zauważ, że jest to niebezpieczne, ponieważ polecenie nie jest czyszczone. Od Ciebie zależy, czy przejrzysz odpowiednią dokumentację dotyczącą modułów „os” i „sys”. Istnieje kilka funkcji (exec * i spawn *), które zrobią podobne rzeczy.


6
Nie mam pojęcia, co miałem na myśli prawie dziesięć lat temu (sprawdź datę!), Ale gdybym musiał zgadywać, nie byłoby to możliwe.
nimish

1
Powinno to teraz wskazywać na subprocessnieco bardziej wszechstronne i przenośne rozwiązanie. Uruchamianie zewnętrznych poleceń jest oczywiście z natury niesportowalne (musisz upewnić się, że polecenie jest dostępne w każdej architekturze, którą musisz obsługiwać), a przekazywanie danych wejściowych przez użytkownika jako zewnętrzne polecenie jest z natury niebezpieczne.
tripleee

1
Zwróć uwagę na znacznik czasu tego faceta: „poprawna” odpowiedź ma 40x głosów i jest odpowiedzią nr 1.
nimish

Jedno rozwiązanie, które zadziałało dla mnie przy uruchamianiu NodeJS.
Nikolay Shindarov

147

Polecam użycie modułu podprocesu zamiast os.system, ponieważ pozwala on na ucieczkę powłoki i dlatego jest znacznie bezpieczniejszy.

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

Jeśli chcesz utworzyć listę z polecenia z parametrami , listę, z której można skorzystać, subprocesskiedy shell=False, a następnie użyj w shlex.splitcelu łatwego wykonania tego docs.python.org/2/library/shlex.html#shlex.split ( jest to zalecany sposób według docs docs.python.org/2/library/subprocess.html#popen-constructor )
Daniel F

6
Jest to niepoprawne: „ robi dla ciebie ucieczkę z powłoki i dlatego jest znacznie bezpieczniejszy ”. podproces nie wykonuje ucieczki powłoki, podproces nie przekazuje polecenia przez powłokę, więc nie ma potrzeby ucieczki powłoki.
Lie Ryan,

143
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 korzyść modułu podprocesu , który dobrze opisały inne odpowiedzi.


10
Popen jest przestarzały na korzyść podprocesu .
Fox Wilson,

Możesz także zapisać swój wynik za pomocą wywołania os.system, ponieważ działa on jak sama powłoka UNIX, jak na przykład os.system ('ls -l> test2.txt')
Stefan Gruenwald

97

Istnieje wiele różnych bibliotek, które pozwalają na wywoływanie zewnętrznych poleceń za pomocą Pythona. Dla każdej biblioteki podałem opis i pokazałem przykład wywołania zewnętrznego polecenia. Poleceniem, którego użyłem jako przykład, jest ls -l(wyświetl wszystkie pliki). Jeśli chcesz dowiedzieć się więcej o którejkolwiek z bibliotek, które wymieniłem i połączyłem dokumentację dla każdej z nich.

Źródła:

To są wszystkie biblioteki:

Mam nadzieję, że pomoże ci to podjąć decyzję, z której biblioteki korzystać :)

podproces

Podproces pozwala na wywoływanie zewnętrznych poleceń i podłączanie ich do ich rur wejściowych / wyjściowych / błędów (stdin, stdout i stderr). Podproces jest domyślnym wyborem do uruchamiania poleceń, ale czasem inne moduły są lepsze.

subprocess.run(["ls", "-l"]) # Run command
subprocess.run(["ls", "-l"], stdout=subprocess.PIPE) # This will run the command and return any output
subprocess.run(shlex.split("ls -l")) # You can also use the shlex library to split the command

os

OS służy do „funkcji zależnych od systemu operacyjnego”. Można go również używać do wywoływania zewnętrznych poleceń za pomocą os.systemi os.popen(Uwaga: Istnieje również podproces.popen). OS zawsze uruchamia powłokę i jest prostą alternatywą dla osób, które nie muszą lub nie wiedzą, jak używać subprocess.run.

os.system("ls -l") # run command
os.popen("ls -l").read() # This will run the command and return any output

sh

sh to interfejs podprocesu, który pozwala wywoływać programy tak, jakby były funkcjami. Jest to przydatne, jeśli chcesz uruchomić polecenie wiele razy.

sh.ls("-l") # Run command normally
ls_cmd = sh.Command("ls") # Save command as a variable
ls_cmd() # Run command as if it were a function

pion

plumbum to biblioteka dla programów w języku Python przypominających skrypty. Możesz wywoływać programy takie jak funkcje jak w sh. Plumbum jest przydatne, jeśli chcesz uruchomić rurociąg bez powłoki.

ls_cmd = plumbum.local("ls -l") # get command
ls_cmd() # run command

oczekiwanie

pexpect pozwala spawnować aplikacje potomne, kontrolować je i znajdować wzorce w wynikach. Jest to lepsza alternatywa dla podprocesu dla poleceń, które oczekują tty na Uniksie.

pexpect.run("ls -l") # Run command as normal
child = pexpect.spawn('scp foo user@example.com:.') # Spawns child application
child.expect('Password:') # When this is the output
child.sendline('mypassword')

tkanina

Fabric to biblioteka Python 2.5 i 2.7. Umożliwia wykonywanie lokalnych i zdalnych poleceń powłoki. Struktura jest prostą alternatywą dla uruchamiania poleceń w bezpiecznej powłoce (SSH)

fabric.operations.local('ls -l') # Run command as normal
fabric.operations.local('ls -l', capture = True) # Run command and receive output

wysłannik

wysłannik jest znany jako „podproces dla ludzi”. Służy jako wygodne opakowanie wokół subprocessmodułu.

r = envoy.run("ls -l") # Run command
r.std_out # get output

polecenia

commandszawiera funkcje otoki dla os.popen, ale został usunięty z Python 3, ponieważ subprocessjest lepszą alternatywą.

Edycja została oparta na komentarzu JF Sebastiana.


74

Zawsze używam fabricdo takich rzeczy jak:

from fabric.operations import local
result = local('ls', capture=True)
print "Content:/n%s" % (result, )

Ale wydaje się, że jest to dobre narzędzie: sh(interfejs podprocesu Python) .

Spójrz na przykład:

from sh import vgdisplay
print vgdisplay()
print vgdisplay('-v')
print vgdisplay(v=True)

73

Sprawdź także bibliotekę Python „pexpect”.

Umożliwia interaktywne sterowanie zewnętrznymi programami / poleceniami, nawet ssh, ftp, telnet itp. Możesz po prostu wpisać coś takiego:

child = pexpect.spawn('ftp 192.168.0.24')

child.expect('(?i)name .*: ')

child.sendline('anonymous')

child.expect('(?i)password')

70

Ze standardową biblioteką

Użyj modułu podprocesu (Python 3):

import subprocess
subprocess.run(['ls', '-l'])

Jest to zalecany standardowy sposób. Jednak bardziej skomplikowane zadania (potoki, dane wyjściowe, dane wejściowe itp.) Mogą być uciążliwe w konstruowaniu i pisaniu.

Uwaga na temat wersji Python: Jeśli nadal używasz Python 2, subprocess.call działa w podobny sposób.

Protip: shlex.split może pomóc analizować polecenia dla run, calli inne subprocessfunkcje w przypadku, gdy nie chcą (lub nie!) Udostępnianie ich w formie list:

import shlex
import subprocess
subprocess.run(shlex.split('ls -l'))

Z zewnętrznymi zależnościami

Jeśli nie masz nic przeciwko zewnętrznym zależnościom, użyj pionu :

from plumbum.cmd import ifconfig
print(ifconfig['wlan0']())

To najlepsze subprocessopakowanie. Jest wieloplatformowy, tzn. Działa zarówno na systemach Windows, jak i systemach uniksowych. Zainstaluj przez pip install plumbum.

Inną popularną biblioteką jest sh :

from sh import ifconfig
print(ifconfig('wlan0'))

Jednak shporzuciłem obsługę systemu Windows, więc nie jest tak niesamowita, jak kiedyś. Zainstaluj przez pip install sh.


69

Jeśli potrzebujesz danych wyjściowych z wywoływanej komendy, możesz użyć subprocess.check_output (Python 2.7+).

>>> subprocess.check_output(["ls", "-l", "/dev/null"])
'crw-rw-rw- 1 root root 1, 3 Oct 18  2007 /dev/null\n'

Zwróć także uwagę na parametr powłoki .

Jeśli shell jest True, określone polecenie zostanie wykonane przez powłokę. Może to być przydatne, jeśli używasz Pythona przede wszystkim do ulepszonego przepływu sterowania, jaki oferuje on w większości powłok systemowych i nadal chcesz wygodnego dostępu do innych funkcji powłoki, takich jak potoki powłoki, symbole wieloznaczne nazw plików, rozszerzanie zmiennych środowiskowych i rozszerzanie ~ do domu użytkownika informator. Jednak należy pamiętać, że sama Python oferuje implementacje wielu shell-jak funkcje (w szczególności glob, fnmatch, os.walk(), os.path.expandvars(),os.path.expanduser() , i shutil).


1
Zauważ, że check_outputwymaga listy, a nie ciągu. Jeśli nie chcesz, aby twoje połączenie było ważne, nie polegaj na cudzysłowach, najprostszym i najbardziej czytelnym sposobem na to subprocess.check_output("ls -l /dev/null".split()).
Bruno Bronosky

56

W ten sposób uruchamiam moje polecenia. Ten kod zawiera wszystko, czego potrzebujesz

from subprocess import Popen, PIPE
cmd = "ls -l ~/"
p = Popen(cmd , shell=True, stdout=PIPE, stderr=PIPE)
out, err = p.communicate()
print "Return code: ", p.returncode
print out.rstrip(), err.rstrip()

3
Myślę, że jest to dopuszczalne w przypadku poleceń zakodowanych na stałe, jeśli zwiększa to czytelność.
Adam Matan

54

Aktualizacja:

subprocess.runjest zalecanym podejściem od wersji Python 3.5, jeśli kod nie musi utrzymywać zgodności z wcześniejszymi wersjami Python. Jest bardziej spójny i oferuje podobną łatwość użycia jak wysłannik. (Rurociągi nie są jednak tak proste. Zobacz to pytanie, jak to zrobić ).

Oto kilka przykładów z dokumentacji .

Uruchom proces:

>>> subprocess.run(["ls", "-l"])  # Doesn't capture output
CompletedProcess(args=['ls', '-l'], returncode=0)

Podnieś po nieudanym biegu:

>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
  ...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1

Przechwytywanie danych wyjściowych:

>>> subprocess.run(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE)
CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0,
stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n')

Oryginalna odpowiedź:

Polecam wypróbowanie Posła . Jest to opakowanie dla podprocesu, który z kolei ma na celu zastąpienie starszych modułów i funkcji. Wysłannik jest procesem dla ludzi.

Przykładowe użycie z README :

>>> r = envoy.run('git config', data='data to pipe in', timeout=2)

>>> r.status_code
129
>>> r.std_out
'usage: git config [options]'
>>> r.std_err
''

Rurociągi też:

>>> r = envoy.run('uptime | pbcopy')

>>> r.command
'pbcopy'
>>> r.status_code
0

>>> r.history
[<Response 'uptime'>]


36

Wywoływanie zewnętrznego polecenia w Pythonie

Prosty, użyj subprocess.run, który zwraca CompletedProcessobiekt:

>>> import subprocess
>>> completed_process = subprocess.run('python --version')
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
>>> completed_process
CompletedProcess(args='python --version', returncode=0)

Dlaczego?

Począwszy od języka Python 3.5 dokumentacja zaleca subprocess.run :

Zalecanym podejściem do wywoływania podprocesów jest użycie funkcji run () dla wszystkich obsługiwanych przypadków użycia. W bardziej zaawansowanych przypadkach użycia można bezpośrednio użyć bazowego interfejsu Popen.

Oto przykład najprostszego możliwego użycia - i robi to dokładnie tak, jak zapytano:

>>> import subprocess
>>> completed_process = subprocess.run('python --version')
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
>>> completed_process
CompletedProcess(args='python --version', returncode=0)

runczeka na pomyślne zakończenie polecenia, a następnie zwraca CompletedProcessobiekt. Zamiast tego może podnieść TimeoutExpired(jeśli podasz timeout=argument) lub CalledProcessError(jeśli zawiedzie i zdasz check=True).

Jak można wywnioskować z powyższego przykładu, stdout i stderr są domyślnie przesyłane do własnego stdout i stderr.

Możemy sprawdzić zwrócony obiekt i zobaczyć wydane polecenie oraz kod powrotu:

>>> completed_process.args
'python --version'
>>> completed_process.returncode
0

Przechwytywanie danych wyjściowych

Jeśli chcesz przechwycić dane wyjściowe, możesz przejść subprocess.PIPEdo odpowiedniego stderrlub stdout:

>>> cp = subprocess.run('python --version', 
                        stderr=subprocess.PIPE, 
                        stdout=subprocess.PIPE)
>>> cp.stderr
b'Python 3.6.1 :: Anaconda 4.4.0 (64-bit)\r\n'
>>> cp.stdout
b''

(Uważam za interesujące i nieco sprzeczne z intuicją, że informacje o wersji są ustawiane na stderr zamiast stdout.)

Przekaż listę poleceń

Można łatwo przejść od ręcznego podawania ciągu poleceń (jak sugeruje to pytanie) do dostarczania ciągu zbudowanego programowo. Nie buduj ciągów programowo. Jest to potencjalny problem bezpieczeństwa. Lepiej założyć, że nie ufasz wkładowi.

>>> import textwrap
>>> args = ['python', textwrap.__file__]
>>> cp = subprocess.run(args, stdout=subprocess.PIPE)
>>> cp.stdout
b'Hello there.\r\n  This is indented.\r\n'

Uwaga, argsnależy przekazywać tylko pozycyjnie.

Pełny podpis

Oto rzeczywisty podpis w źródle i pokazany przez help(run):

def run(*popenargs, input=None, timeout=None, check=False, **kwargs):

popenargsI kwargssą przekazywane do Popenkonstruktora. inputmoże być ciągiem bajtów (lub Unicode, jeśli określono kodowanie lub universal_newlines=True), które będą przesyłane do standardowego wejścia podprocesu.

Dokumentacja opisuje timeout=i check=Truelepiej niż mogłem:

Argument limitu czasu jest przekazywany do Popen.communicate (). Jeśli upłynie limit czasu, proces potomny zostanie zabity i czeka na. Wyjątek TimeoutExpired zostanie ponownie zgłoszony po zakończeniu procesu potomnego.

Jeśli sprawdzanie jest prawdziwe, a proces kończy się z niezerowym kodem wyjścia, zgłoszony zostanie wyjątek CalledProcessError. Atrybuty tego wyjątku zawierają argumenty, kod wyjścia oraz stdout i stderr, jeśli zostały przechwycone.

i ten przykład check=Truejest lepszy niż ten, który mógłbym wymyślić:

>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
  ...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1

Rozszerzony podpis

Oto rozwinięty podpis, jak podano w dokumentacji:

subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, 
shell=False, cwd=None, timeout=None, check=False, encoding=None, 
errors=None)

Zauważ, że oznacza to, że tylko lista argumentów powinna być przekazywana pozycyjnie. Przekaż pozostałe argumenty jako argumenty słów kluczowych.

Popen

Kiedy Popenzamiast tego użyć ? Z trudem znajdowałbym przypadek użycia na podstawie samych argumentów. Bezpośrednie użycie Popendaje jednak dostęp do jego metod, w tym poll„send_signal”, „terminate” i „wait”.

Oto Popenpodpis podany w źródle . Myślę, że jest to najbardziej precyzyjna enkapsulacja informacji (w przeciwieństwie do help(Popen)):

def __init__(self, args, bufsize=-1, executable=None,
             stdin=None, stdout=None, stderr=None,
             preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS,
             shell=False, cwd=None, env=None, universal_newlines=False,
             startupinfo=None, creationflags=0,
             restore_signals=True, start_new_session=False,
             pass_fds=(), *, encoding=None, errors=None):

Ale bardziej pouczające jest dokumentacja :Popen

subprocess.Popen(args, bufsize=-1, executable=None, stdin=None,
                 stdout=None, stderr=None, preexec_fn=None, close_fds=True,
                 shell=False, cwd=None, env=None, universal_newlines=False,
                 startupinfo=None, creationflags=0, restore_signals=True,
                 start_new_session=False, pass_fds=(), *, encoding=None, errors=None)

Uruchom program potomny w nowym procesie. W systemie POSIX klasa używa zachowania podobnego do os.execvp () w celu wykonania programu potomnego. W systemie Windows klasa korzysta z funkcji Windows CreateProcess (). Argumenty skierowane do Popena są następujące.

Zrozumienie pozostałej dokumentacji Popenzostanie pozostawione jako ćwiczenie dla czytelnika.


Prosty przykład dwukierunkowej komunikacji między procesem podstawowym a podprocesem można znaleźć tutaj: stackoverflow.com/a/52841475/1349673
James Hirschorn

Pierwszy przykład prawdopodobnie powinien mieć shell=True(lub jeszcze lepiej) przekazać polecenie jako listę.
tripleee

35

os.systemjest OK, ale trochę przestarzałe. To również nie jest bardzo bezpieczne. Zamiast tego spróbuj subprocess. subprocessnie wywołuje sh bezpośrednio i dlatego jest bezpieczniejszy niżos.system .

Uzyskaj więcej informacji tutaj .


2
Chociaż zgadzam się z ogólną rekomendacją, subprocessnie usuwa wszystkich problemów związanych z bezpieczeństwem i ma pewne nieznośne problemy.
tripleee

33

Jest też Plumbum

>>> from plumbum import local
>>> ls = local["ls"]
>>> ls
LocalCommand(<LocalPath /bin/ls>)
>>> ls()
u'build.py\ndist\ndocs\nLICENSE\nplumbum\nREADME.rst\nsetup.py\ntests\ntodo.txt\n'
>>> notepad = local["c:\\windows\\notepad.exe"]
>>> notepad()                                   # Notepad window pops up
u''                                             # Notepad window is closed by user, command returns

28

Posługiwać się:

import os

cmd = 'ls -al'

os.system(cmd)

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

Dla bardziej osfunkcji, tutaj jest dokumentacja.


2
jest również przestarzałe. użyj podprocesu
Corey Goldberg,

28

Może to być takie proste:

import os
cmd = "your command"
os.system(cmd)

1
Nie wskazuje to na wady, które zostały wyjaśnione bardziej szczegółowo w PEP-324 . Dokumentacja os.systemwyraźnie zaleca unikanie tego na korzyść subprocess.
tripleee

25

Bardzo podoba mi się shell_command ze względu na jego prostotę. Jest zbudowany na module podprocesu.

Oto przykład z dokumentacji:

>>> from shell_command import shell_call
>>> shell_call("ls *.py")
setup.py  shell_command.py  test_shell_command.py
0
>>> shell_call("ls -l *.py")
-rw-r--r-- 1 ncoghlan ncoghlan  391 2011-12-11 12:07 setup.py
-rw-r--r-- 1 ncoghlan ncoghlan 7855 2011-12-11 16:16 shell_command.py
-rwxr-xr-x 1 ncoghlan ncoghlan 8463 2011-12-11 16:17 test_shell_command.py
0

24

Jest jeszcze jedna różnica, o której wcześniej nie wspomniano.

subprocess.Popenwykonuje <polecenie> jako podproces. W moim przypadku muszę wykonać plik <a>, który musi komunikować się z innym programem, <b>.

Próbowałem podprocesu i wykonanie zakończyło się powodzeniem. Jednak <b> nie mógł komunikować się z <a>. Wszystko działa normalnie, gdy uruchamiam oba z terminala.

Jeszcze jedno: (UWAGA: kwrite zachowuje się inaczej niż inne aplikacje. Jeśli wypróbujesz poniższe ustawienia w przeglądarce Firefox, wyniki nie będą takie same.)

Jeśli spróbujesz os.system("kwrite"), przepływ programu zawiesza się, dopóki użytkownik nie zamknie kwrite. Żeby to pokonać, próbowałem zamiast tegoos.system(konsole -e kwrite) . Ten program czasowy nadal płynął, ale kwrite stał się podprocesem konsoli.

Każdy uruchamia kwrite, który nie jest podprocesem (tzn. Na monitorze systemu musi pojawiać się na skrajnej lewej krawędzi drzewa).


1
Co rozumiesz przez „ktoś uruchamia kwrite nie będąc podprocesiem” ?
Peter Mortensen

23

os.systemnie pozwala na przechowywanie wyników, więc jeśli chcesz zapisać wyniki na jakiejś liście lub coś, subprocess.calldziała.


22

subprocess.check_calljest wygodny, jeśli nie chcesz testować zwracanych wartości. Zgłasza wyjątek dla każdego błędu.


22

Zwykle używam podprocesu razem z shlex (do obsługi ucieczki cytowanych ciągów):

>>> import subprocess, shlex
>>> command = 'ls -l "/your/path/with spaces/"'
>>> call_params = shlex.split(command)
>>> print call_params
["ls", "-l", "/your/path/with spaces/"]
>>> subprocess.call(call_params)

17

Bezwstydna wtyczka, napisałem bibliotekę dla tego: P https://github.com/houqp/shell.py

Na razie jest to po prostu opakowanie na popen i shlex. Obsługuje także polecenia potokowe, dzięki czemu można łatwiej łączyć łańcuchy poleceń w języku Python. Możesz więc robić takie rzeczy jak:

ex('echo hello shell.py') | "awk '{print $2}'"

16

W systemie Windows można po prostu zaimportować subprocessmoduł i uruchomić zewnętrznych poleceń poprzez wywołanie subprocess.Popen(), subprocess.Popen().communicate()a subprocess.Popen().wait()jak poniżej:

# Python script to run a command line
import subprocess

def execute(cmd):
    """
        Purpose  : To execute a command and return exit status
        Argument : cmd - command to execute
        Return   : exit_code
    """
    process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    (result, error) = process.communicate()

    rc = process.wait()

    if rc != 0:
        print "Error: failed to execute command:", cmd
        print error
    return result
# def

command = "tasklist | grep python"
print "This process detail: \n", execute(command)

Wynik:

This process detail:
python.exe                     604 RDP-Tcp#0                  4      5,660 K

15

W systemie Linux, jeśli chcesz wywołać zewnętrzną komendę, która wykona się niezależnie (będzie działać po zakończeniu skryptu python), możesz użyć prostej kolejki jako buforu zadań lub komendy at

Przykład z buforem zadań:

import os
os.system('ts <your-command>')

Uwagi na temat buforowania zadań ( ts):

  1. Możesz ustawić liczbę równoległych procesów do uruchomienia („slotów”) za pomocą:

    ts -S <number-of-slots>

  2. Instalacja tsnie wymaga uprawnień administratora. Możesz pobrać i skompilować go ze źródła w prosty sposób make, dodać go do swojej ścieżki i gotowe.


1
tsnie jest standardem w żadnej znanej dystrybucji, chociaż wskaźnik do atjest raczej przydatny. Prawdopodobnie powinieneś również wspomnieć batch. Podobnie jak gdzie indziej, os.system()zalecenie powinno przynajmniej zawierać wzmiankę o subprocesszalecanym zastąpieniu.
tripleee

15

Możesz użyć Popen, a następnie możesz sprawdzić status procedury:

from subprocess import Popen

proc = Popen(['ls', '-l'])
if proc.poll() is None:
    proc.kill()

Sprawdź podproces . Otwórz .


15

Aby pobrać identyfikator sieci z OpenStack Neutron :

#!/usr/bin/python
import os
netid = "nova net-list | awk '/ External / { print $2 }'"
temp = os.popen(netid).read()  /* Here temp also contains new line (\n) */
networkId = temp.rstrip()
print(networkId)

Wyjście nova netto liście

+--------------------------------------+------------+------+
| ID                                   | Label      | CIDR |
+--------------------------------------+------------+------+
| 431c9014-5b5d-4b51-a357-66020ffbb123 | test1      | None |
| 27a74fcd-37c0-4789-9414-9531b7e3f126 | External   | None |
| 5a2712e9-70dc-4b0e-9281-17e02f4684c9 | management | None |
| 7aa697f5-0e60-4c15-b4cc-9cb659698512 | Internal   | None |
+--------------------------------------+------------+------+

Wyjście wydruku (networkId)

27a74fcd-37c0-4789-9414-9531b7e3f126

Nie powinieneś polecać os.popen()w 2016 roku. Skrypt Awk można łatwo zastąpić rodzimym kodem Python.
tripleee
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.