Jak wywołać zewnętrzne polecenie (tak, jakbym napisał je w powłoce Unixa lub w wierszu poleceń systemu Windows) z poziomu skryptu Python?
Jak wywołać zewnętrzne polecenie (tak, jakbym napisał je w powłoce Unixa lub w wierszu poleceń systemu Windows) z poziomu skryptu Python?
Odpowiedzi:
Spójrz na moduł podprocesu w standardowej bibliotece:
import subprocess
subprocess.run(["ls", "-l"])
Zaletą subprocess
vs. system
jest to, że jest bardziej elastyczny (można uzyskać stdout
, stderr
, „prawdziwy” kod statusu, lepsza obsługa błędów, itp ...).
Oficjalna dokumentacja zaleca subprocess
moduł nad alternatywą os.system()
:
subprocess
Moduł 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 subprocess
dokumentacji może mieć kilka pomocnych recepty.
W przypadku wersji Python wcześniejszych niż 3.5 użyj call
:
import subprocess
subprocess.call(["ls", "-l"])
shell=True
, aby to zadziałało.
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
for
pętli, jak to zrobić bez blokowania mojego skryptu python? Nie dbam o wynik polecenia, chcę po prostu uruchomić wiele z nich.
subprocess
kiedy shell=False
, a następnie użyj w shlex.split
celu łatwego wykonania tej czynności docs.python.org/2/library/shlex.html#shlex.split
Oto podsumowanie sposobów wywoływania programów zewnętrznych oraz zalety i wady każdego z nich:
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ę .
stream = os.popen("some_command with args")
zrobi to samo co os.system
poza 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ę .
Popen
Klasa subprocess
modułu. Ma to na celu zastąpienie, os.popen
ale 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ę .
call
Funkcji z subprocess
modułu. Jest to w zasadzie tak samo jak Popen
klasa 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ę .
Jeśli korzystasz z języka Python 3.5 lub nowszego, możesz użyć nowej subprocess.run
funkcji, która jest bardzo podobna do powyższej, ale jeszcze bardziej elastyczna i zwraca CompletedProcess
obiekt po zakończeniu wykonywania polecenia.
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.
subprocess
Moduł 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.
open
.
subprocess.run()
. docs.python.org/3.5/library/subprocess.html#subprocess.run
subprocess.run(..)
co dokładnie robi „To domyślnie nie przechwytuje stdout ani stderr”. sugerować? A co z subprocess.check_output(..)
STDERR?
echo
przed ciągiem przekazanym do Popen
? Tak będzie pełne polecenie echo my mama didnt love me && rm -rf /
.
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”.
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 stdout
danymi w potoku. W rzeczywistości możesz po prostu pominąć te parametry ( stdout=
i stderr=
) i będzie się tak zachowywał os.system()
.
.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,
p.stdout.readline()
(uwaga: nie s
na 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)
Popen
prostych zadań. To również niepotrzebnie określa shell=True
. Wypróbuj jedną z subprocess.run()
odpowiedzi.
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.
DETACHED_PROCESS
w creationflags
unika tego poprzez zapobieganie dziecko od dziedziczenia lub tworząc konsolę. Jeśli zamiast tego chcesz nową konsolę, użyj CREATE_NEW_CONSOLE
(0x00000010).
os.devnull
ponieważ 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.
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.
subprocess
nieco 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.
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'])
subprocess
kiedy shell=False
, a następnie użyj w shlex.split
celu ł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 )
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.
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.system
i 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ół subprocess
modułu.
r = envoy.run("ls -l") # Run command
r.std_out # get output
polecenia
commands
zawiera funkcje otoki dla os.popen
, ale został usunięty z Python 3, ponieważ subprocess
jest lepszą alternatywą.
Edycja została oparta na komentarzu JF Sebastiana.
Zawsze używam fabric
do 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)
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')
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
, call
i inne subprocess
funkcje w przypadku, gdy nie chcą (lub nie!) Udostępnianie ich w formie list:
import shlex
import subprocess
subprocess.run(shlex.split('ls -l'))
Jeśli nie masz nic przeciwko zewnętrznym zależnościom, użyj pionu :
from plumbum.cmd import ifconfig
print(ifconfig['wlan0']())
To najlepsze subprocess
opakowanie. 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 sh
porzuciłem obsługę systemu Windows, więc nie jest tak niesamowita, jak kiedyś. Zainstaluj przez pip install sh
.
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ściglob
,fnmatch
,os.walk()
,os.path.expandvars()
,os.path.expanduser()
, ishutil
).
check_output
wymaga 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())
.
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()
subprocess.run
jest 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')
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'>]
Wywoływanie zewnętrznego polecenia w Pythonie
Prosty, użyj subprocess.run
, który zwraca CompletedProcess
obiekt:
>>> 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)
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)
run
czeka na pomyślne zakończenie polecenia, a następnie zwraca CompletedProcess
obiekt. 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
Jeśli chcesz przechwycić dane wyjściowe, możesz przejść subprocess.PIPE
do odpowiedniego stderr
lub 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.)
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, args
należy przekazywać tylko pozycyjnie.
Oto rzeczywisty podpis w źródle i pokazany przez help(run)
:
def run(*popenargs, input=None, timeout=None, check=False, **kwargs):
popenargs
I kwargs
są przekazywane do Popen
konstruktora. input
moż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=True
lepiej 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=True
jest 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
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.
Kiedy Popen
zamiast tego użyć ? Z trudem znajdowałbym przypadek użycia na podstawie samych argumentów. Bezpośrednie użycie Popen
daje jednak dostęp do jego metod, w tym poll
„send_signal”, „terminate” i „wait”.
Oto Popen
podpis 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 Popen
zostanie pozostawione jako ćwiczenie dla czytelnika.
shell=True
(lub jeszcze lepiej) przekazać polecenie jako listę.
os.system
jest OK, ale trochę przestarzałe. To również nie jest bardzo bezpieczne. Zamiast tego spróbuj subprocess
. subprocess
nie wywołuje sh bezpośrednio i dlatego jest bezpieczniejszy niżos.system
.
Uzyskaj więcej informacji tutaj .
subprocess
nie usuwa wszystkich problemów związanych z bezpieczeństwem i ma pewne nieznośne problemy.
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
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 os
funkcji, tutaj jest dokumentacja.
Może to być takie proste:
import os
cmd = "your command"
os.system(cmd)
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
Jest jeszcze jedna różnica, o której wcześniej nie wspomniano.
subprocess.Popen
wykonuje <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).
os.system
nie pozwala na przechowywanie wyników, więc jeśli chcesz zapisać wyniki na jakiejś liście lub coś, subprocess.call
działa.
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)
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}'"
W systemie Windows można po prostu zaimportować subprocess
moduł 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
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
):
Możesz ustawić liczbę równoległych procesów do uruchomienia („slotów”) za pomocą:
ts -S <number-of-slots>
Instalacja ts
nie 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.
ts
nie jest standardem w żadnej znanej dystrybucji, chociaż wskaźnik do at
jest raczej przydatny. Prawdopodobnie powinieneś również wspomnieć batch
. Podobnie jak gdzie indziej, os.system()
zalecenie powinno przynajmniej zawierać wzmiankę o subprocess
zalecanym zastąpieniu.
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
os.popen()
w 2016 roku. Skrypt Awk można łatwo zastąpić rodzimym kodem Python.
echo $PATH
za pomocącall(["echo", "$PATH"])
, ale po prostu powtórzyło literalny ciąg$PATH
zamiast 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.