Chcę napisać funkcję, która wykona polecenie powłoki i zwróci jego wynik jako ciąg , bez względu na to, czy jest to komunikat o błędzie czy sukcesie. Chcę tylko uzyskać ten sam wynik, który uzyskałbym za pomocą wiersza poleceń.
Jaki byłby przykład kodu, który zrobiłby coś takiego?
Na przykład:
def run_command(cmd):
# ??????
print run_command('mysqladmin create test -uroot -pmysqladmin12')
# Should output something like:
# mysqladmin: CREATE DATABASE failed; error: 'Can't create database 'test'; database exists'
python
shell
subprocess
Srebrne światło
źródło
źródło
Odpowiedzi:
Odpowiedź na to pytanie zależy od używanej wersji języka Python. Najprostszym podejściem jest użycie
subprocess.check_output
funkcji:check_output
uruchamia pojedynczy program, który przyjmuje tylko argumenty jako dane wejściowe. 1 Zwraca wynik dokładnie tak, jak wydrukowanostdout
. Jeśli chcesz zapisać dane wejściowestdin
, przejdź do sekcjirun
lubPopen
. Jeśli chcesz wykonywać złożone polecenia powłoki, zobacz uwagę nashell=True
końcu tej odpowiedzi.Ta
check_output
funkcja działa na prawie wszystkich wersjach Pythona, które są nadal w powszechnym użyciu (2.7+). 2 Jednak w przypadku nowszych wersji nie jest to już zalecane podejście.Nowoczesne wersje Python (3.5 lub nowszy):
run
Jeśli używasz języka Python 3.5 lub nowszego i nie potrzebujesz kompatybilności wstecznej , nowa
run
funkcja jest zalecana. Zapewnia bardzo ogólny interfejs API wysokiego poziomu dlasubprocess
modułu. Aby przechwycić dane wyjściowe programu, przekażsubprocess.PIPE
flagę dostdout
argumentu słowa kluczowego. Następnie uzyskaj dostęp dostdout
atrybutu zwróconegoCompletedProcess
obiektu:Zwracana wartość to
bytes
obiekt, więc jeśli potrzebujesz odpowiedniego ciągu, musiszdecode
go. Zakładając, że wywoływany proces zwraca łańcuch zakodowany w UTF-8:Wszystko to można skompresować do jednej linijki:
Jeśli chcesz przekazać dane wejściowe do procesu
stdin
, przekażbytes
obiekt doinput
argumentu słowa kluczowego:Błędy można wychwytywać, przekazując
stderr=subprocess.PIPE
(przechwytywanie doresult.stderr
) lubstderr=subprocess.STDOUT
(przechwytywanieresult.stdout
wraz ze zwykłym wyjściem). Gdy bezpieczeństwo nie stanowi problemu, możesz także uruchamiać bardziej złożone polecenia powłoki, przekazującshell=True
zgodnie z opisem w uwagach poniżej.Dodaje to nieco złożoności w porównaniu do starego sposobu robienia rzeczy. Ale myślę, że jest to opłacalne: teraz możesz zrobić prawie wszystko, co musisz zrobić z
run
samą funkcją.Starsze wersje Pythona (2.7-3.4):
check_output
Jeśli używasz starszej wersji Pythona lub potrzebujesz skromnej kompatybilności wstecznej, prawdopodobnie możesz użyć tej
check_output
funkcji w skrócie opisany powyżej. Jest dostępny od wersji Python 2.7.Pobiera te same argumenty co
Popen
(patrz poniżej) i zwraca ciąg zawierający dane wyjściowe programu. Początek tej odpowiedzi zawiera bardziej szczegółowy przykład użycia. W Pythonie 3.5 i nowszychcheck_output
jest równoważne wykonywaniu zarun
pomocącheck=True
istdout=PIPE
i zwracaniu tylkostdout
atrybutu.Możesz przekazać,
stderr=subprocess.STDOUT
aby upewnić się, że komunikaty o błędach są uwzględnione w zwróconych danych wyjściowych - ale w niektórych wersjach przekazywaniestderr=subprocess.PIPE
do Pythonacheck_output
może powodować zakleszczenia . Gdy bezpieczeństwo nie stanowi problemu, możesz także uruchamiać bardziej złożone polecenia powłoki, przekazującshell=True
zgodnie z opisem w uwagach poniżej.Jeśli potrzebujesz potokować
stderr
lub przekazywać dane wejściowe do procesu,check_output
nie będzie to do zadania.Popen
W takim przypadku zobacz poniższe przykłady.Złożone aplikacje i starsze wersje Pythona (2.6 i poniżej):
Popen
Jeśli potrzebujesz głębokiej kompatybilności wstecznej lub potrzebujesz bardziej wyrafinowanej funkcjonalności niż
check_output
zapewnia, będziesz musiał pracować bezpośrednio zPopen
obiektami, które zawierają niskopoziomowy interfejs API dla podprocesów.Popen
Konstruktor przyjmuje zarówno pojedyncze polecenie , bez argumentów lub listy zawierającej polecenie jako jego pierwszej pozycji, a po dowolnej liczbie argumentów każdej jako oddzielny element na liście.shlex.split
może pomóc w analizie ciągów znaków w odpowiednio sformatowanych listach.Popen
obiekty akceptują również wiele różnych argumentów do zarządzania procesami IO i konfiguracji niskiego poziomu.Wysyłanie danych wejściowych i przechwytywanie danych wyjściowych
communicate
jest prawie zawsze preferowaną metodą. Jak w:Lub
Jeśli ustawisz
stdin=PIPE
,communicate
pozwala również na przekazywanie danych do procesu poprzezstdin
:UWAGA Aaron Hall odpowiedź , która wskazuje, że w niektórych systemach, może trzeba ustawić
stdout
,stderr
istdin
wszystkoPIPE
(lubDEVNULL
), aby dostać sięcommunicate
do pracy w ogóle.W niektórych rzadkich przypadkach może być konieczne przechwytywanie danych wyjściowych w czasie rzeczywistym. Odpowiedź Vartec sugeruje drogę naprzód, ale metody inne niż
communicate
podatne na impas, jeśli nie są stosowane ostrożnie.Podobnie jak w przypadku wszystkich powyższych funkcji, gdy bezpieczeństwo nie stanowi problemu, można uruchamiać bardziej złożone polecenia powłoki, przekazując
shell=True
.Notatki
1. Uruchamianie poleceń powłoki:
shell=True
argumentNormalnie, każde wywołanie
run
,check_output
lubPopen
konstruktor wykonuje jeden program . Oznacza to, że nie ma żadnych fajnych rur typu bash. Jeśli chcesz uruchamiać złożone polecenia powłoki, możesz przekazaćshell=True
, które obsługują wszystkie trzy funkcje.Takie postępowanie budzi jednak obawy dotyczące bezpieczeństwa . Jeśli robisz coś więcej niż lekkie skrypty, lepiej byłoby wywołać każdy proces osobno i przekazać dane wyjściowe z każdego z nich jako dane wejściowe do następnego, za pośrednictwem
Lub
Pokusa bezpośredniego połączenia rur jest silna; Oprzeć się. W przeciwnym razie będziesz prawdopodobnie zobaczyć zakleszczenia lub trzeba zrobić hacky rzeczy jak ta .
2. Uwagi dotyczące Unicode
check_output
zwraca ciąg znaków w Python 2, alebytes
obiekt w Python 3. Warto poświęcić chwilę, aby dowiedzieć się o Unicode, jeśli jeszcze tego nie zrobiłeś.źródło
check_output()
icommunicate()
musisz poczekać, aż proces się zakończy, apoll()
otrzymasz wynik w postaci, w jakiej się pojawia. Naprawdę zależy od tego, czego potrzebujesz.out
była typu<class 'bytes'>
. Aby uzyskać wynik jako ciąg, musiałem go zdekodować przed wydrukowaniem w następujący sposób:out.decode("utf-8")
shell=True
? Mi to pasuje. Nie potrzebujesz,shlex.split
kiedy zdaszshell=True
.shlex.split
jest dla poleceń innych niż powłoki. Myślę, że zamierzam to wyciągnąć, bo to błotniste wody.universal_newlines=True
który pozwala przekazać i wydobyć ciągi Unicode w domyślnym kodowaniu systemu. W wersji 3.7 zmieniono jego nazwę na bardziej sensownątext=True
.encoding
parametrusubprocess.run
zamiastresult.stdout.decode('utf-8')
, można użyćsubprocess.run(['ls', '-l'], stdout=subprocess.PIPE, encoding='utf-8')
.Jest to o wiele łatwiejsze, ale działa tylko na systemach Unix (w tym Cygwin) i Python2.7.
Zwraca krotkę z (return_value, output).
W przypadku rozwiązania, które działa zarówno w Python2, jak i Python3, użyj
subprocess
modułu:źródło
Coś w tym stylu:
Zauważ, że przekierowuję stderr na standardowe wyjście, może to nie być dokładnie to, czego chcesz, ale chcę także komunikatów o błędach.
Ta funkcja zwraca linijkę po linii (zwykle trzeba czekać na zakończenie podprocesu, aby uzyskać wynik jako całość).
W twoim przypadku użycie byłoby:
źródło
wait
icall
funkcji.PIPE
podać wartośćstdin
i zamknąć ten plik, gdy tylkoPopen
powróci.retcode
jest0
. Czek powinien byćif retcode is not None
. Nie należy uzyskując puste łańcuchy (nawet pusta jest to co najmniej jeden symbol „\ n”)if line: yield line
. Zadzwońp.stdout.close()
na koniec..readlines()
nie powróci, dopóki wszystkie dane wyjściowe nie zostaną odczytane, a zatem ulegnie awarii dla dużych danych wyjściowych, które nie mieszczą się w pamięci. Również, aby uniknąć utraty buforowanych danych po wyjściu z podprocesu, powinien istnieć analogif retcode is not None: yield from p.stdout.readlines(); break
Odpowiedź Vartec nie odczytuje wszystkich wierszy, więc stworzyłem wersję, która:
Użycie jest takie samo, jak zaakceptowana odpowiedź:
źródło
return iter(p.stdout.readline, b'')
zamiast pętli whilep.stdout.readline()
zwrócić niepuste, wcześniej buforowane dane wyjściowe, nawet jeśli proces potomny już zakończył działanie (p.poll()
nie jestNone
).To trudne, ale bardzo proste rozwiązanie, które działa w wielu sytuacjach:
Plik tymczasowy (tutaj jest tmp) jest tworzony z danymi wyjściowymi polecenia i można z niego odczytać pożądane dane wyjściowe.
Dodatkowa uwaga z komentarzy: Możesz usunąć plik tmp w przypadku zlecenia jednorazowego. Jeśli musisz to zrobić kilka razy, nie musisz usuwać tmp.
źródło
mktemp
aby działał w sytuacjach gwintowanych - tak myślęos.remove('tmp')
aby uczynić ją „bezplikową”.Miałem ten sam problem, ale wymyśliłem bardzo prosty sposób:
Mam nadzieję, że to pomaga
Uwaga: To rozwiązanie jest specyficzne dla Python3, ponieważ
subprocess.getoutput()
nie działa w Python2źródło
Możesz użyć następujących poleceń, aby uruchomić dowolne polecenie powłoki. Użyłem ich na Ubuntu.
Uwaga: Jest to przestarzałe od Pythona 2.6. Teraz musisz użyć
subprocess.Popen
. Poniżej znajduje się przykładźródło
os.popen()
jest przestarzałe w Pythonie 2.6, ale to nie przestarzałe w Pythonie 3.x, ponieważ w 3.x jest realizowane za pomocąsubprocess.Popen()
.Twój przebieg może się różnić, próbowałem obrócić @ senderle do rozwiązania Vartec w Windows na Pythonie 2.6.5, ale otrzymywałem błędy i żadne inne rozwiązanie nie działało. Mój błąd to:
WindowsError: [Error 6] The handle is invalid
.Odkryłem, że muszę przypisać PIPE do każdego uchwytu, aby uzyskać oczekiwany wynik wyjściowy - następujące działało dla mnie.
i wywołujemy tak: (
[0]
pobiera pierwszy element krotkistdout
):Po dowiedzeniu się więcej, uważam, że potrzebuję tych argumentów potoku, ponieważ pracuję nad niestandardowym systemem, który używa różnych uchwytów, więc musiałem bezpośrednio kontrolować wszystkie standardowe std.
Aby zatrzymać wyskakujące okienka konsoli (w systemie Windows), wykonaj następujące czynności:
źródło
DEVNULL
zamiast zsubprocess.PIPE
, jeśli nie zapisu / odczytu z potoku w przeciwnym razie może zawiesić proces potomny.Miałem nieco inny smak tego samego problemu z następującymi wymaganiami:
powyższego słowa kluczowego „fed”
Połączyłem i poprawiłem poprzednie odpowiedzi, aby uzyskać następujące:
Ten kod zostałby wykonany tak samo jak poprzednie odpowiedzi:
źródło
p.poll()
bez przerwy między połączeniami, marnowalibyśmy cykle procesora, wywołując tę funkcję miliony razy. Zamiast tego „dławimy” naszą pętlę, informując system operacyjny, że nie musimy się martwić przez następną 1/10 sekundy, aby mógł wykonywać inne zadania. (Możliwe, że p.poll () również śpi, co powoduje, że nasza instrukcja sleep jest zbędna).Podział początkowej komendy dla
subprocess
może być trudny i kłopotliwy.Użyj,
shlex.split()
aby sobie pomóc.Przykładowe polecenie
git log -n 5 --since "5 years ago" --until "2 year ago"
Kod
Bez
shlex.split()
kodu wyglądałoby to następującoźródło
shlex.split()
jest wygodą, szczególnie jeśli nie wiesz, jak dokładnie działa cytowanie w powłoce; ale ręczne przekonwertowanie tego ciągu na listę['git', 'log', '-n', '5', '--since', '5 years ago', '--until', '2 year ago']
wcale nie jest trudne, jeśli rozumiesz cytowanie.Jeśli musisz uruchomić polecenie powłoki na wielu plikach, to załatwiło sprawę.
Edycja: Właśnie zobaczyłem rozwiązanie Maxa Perssona z sugestią JF Sebastiana. Śledziliśmy to i wprowadziliśmy to.
źródło
Popen
akceptuje albo ciąg znaków, ale wtedy potrzebujeszshell=True
albo listę argumentów, w którym to przypadku powinieneś podać['nm', filename]
zamiast łańcucha. To drugie jest preferowane, ponieważ powłoka dodaje złożoności, nie podając tutaj żadnej wartości. Przekazywanie ciągu bezshell=True
najwyraźniej działa w systemie Windows, ale może się to zmienić w każdej kolejnej wersji Pythona.Według @senderle, jeśli używasz Python3.6 jak ja:
Będzie działał dokładnie tak, jak uruchomisz polecenie w bash
źródło
check=True, universal_newlines=True
. Innymi słowy,subprocess.run()
robi już wszystko, co robi Twój kod.Jeśli korzystasz z
subprocess
modułu python, możesz osobno obsługiwać STDOUT, STDERR i kod powrotu polecenia. Możesz zobaczyć przykład pełnej implementacji programu wywołującego polecenia. Oczywiście możesz go przedłużyćtry..except
jeśli chcesz.Poniższa funkcja zwraca kod STDOUT, STDERR i Return, aby można było obsługiwać je w innym skrypcie.
źródło
subprocess.run()
. Nie wymyślaj koła ponownie.np. wykonaj ('ls -ahl') rozróżniając trzy / cztery możliwe zwroty i platformy OS:
funkcja poniżej
źródło
Dane wyjściowe można przekierować do pliku tekstowego, a następnie odczytać.
źródło
stdout=temp_file
?ecls.exe
wydaje się, że wdrażasz inne narzędzie wiersza poleceń, więc prosty sposób czasami nie działał.właśnie napisałem mały skrypt bash, aby to zrobić za pomocą curl
https://gist.github.com/harish2704/bfb8abece94893c53ce344548ead8ba5
źródło