Na moim komputerze lokalnym uruchamiam skrypt Pythona, który zawiera tę linię
bashCommand = "cwm --rdf test.rdf --ntriples > test.nt"
os.system(bashCommand)
To działa dobrze.
Następnie uruchamiam ten sam kod na serwerze i pojawia się następujący komunikat o błędzie
'import site' failed; use -v for traceback
Traceback (most recent call last):
File "/usr/bin/cwm", line 48, in <module>
from swap import diag
ImportError: No module named swap
Więc to, co zrobiłem, to wstawienie, print bashCommand
które wypisuje mnie niż polecenie w terminalu przed uruchomieniem go os.system()
.
Oczywiście otrzymuję ponownie błąd (spowodowany przez os.system(bashCommand)
), ale przed tym błędem wypisuje polecenie w terminalu. Potem właśnie skopiowałem to wyjście i wkleiłem kopię do terminala i wcisnąłem enter i działa ...
Czy ktoś ma pojęcie, co się dzieje?
cwm
. Może masz jakąś konfigurację,.bashrc
która konfiguruje środowisko do interaktywnego korzystania z bash?cwm
. A może istnieje PATH, acwm
nazywa się inną wersję . Lub różne wersje Pythona. Naprawdę trudno to rozgryźć bez dostępu do maszyny ...Odpowiedzi:
Nie używać
os.system
. Zostało wycofane na korzyść podprocesu . Od docs : „Moduł ten ma na celu zastąpić kilka starsze moduły i funkcje:os.system
,os.spawn
”.Jak w twoim przypadku:
źródło
cd 'path\to\somewhere'
kolejną komendę bash, która musiała gdzieś tam zostać uruchomiona. @ user225312cwd
argumentu do Popena:subprocess.Popen(..., cwd='path\to\somewhere')
stdout=file
w tym przypadku przekierowuje dane wyjściowe do pliku. Implementuje> file
). Błędem byłoby przekazać..., '>', 'file']
ostatnie polecenie oczekujące przekierowania (nie będzie działać bez powłoki, a jeśli używasz powłoki, należy przekazać polecenie jako ciąg znaków)Aby nieco rozwinąć wcześniejsze odpowiedzi tutaj, istnieje wiele szczegółów, które są często pomijane.
subprocess.run()
nadsubprocess.check_call()
i przyjaciele ponadsubprocess.call()
ponadsubprocess.Popen()
ponados.system()
ponados.popen()
text=True
, aliasuniversal_newlines=True
.shell=True
czyshell=False
i jak zmienia cytowanie i dostępności udogodnień powłoki.sh
i BashTematy te zostały omówione bardziej szczegółowo poniżej.
Wolę
subprocess.run()
lubsubprocess.check_call()
Ta
subprocess.Popen()
funkcja to koń roboczy niskiego poziomu, ale jej prawidłowe użycie jest trudne i kończy się kopiowanie / wklejanie wielu wierszy kodu ... które dogodnie istnieją już w standardowej bibliotece jako zestaw funkcji opakowania wyższego poziomu do różnych celów, które zostały przedstawione bardziej szczegółowo poniżej.Oto akapit z dokumentacji :
Niestety dostępność tych funkcji otoki różni się w zależności od wersji Pythona.
subprocess.run()
został oficjalnie wprowadzony w Pythonie 3.5. Ma to zastąpić wszystkie poniższe.subprocess.check_output()
został wprowadzony w Pythonie 2.7 / 3.1. Jest to w zasadzie odpowiedniksubprocess.run(..., check=True, stdout=subprocess.PIPE).stdout
subprocess.check_call()
został wprowadzony w Pythonie 2.5. Jest to w zasadzie odpowiedniksubprocess.run(..., check=True)
subprocess.call()
został wprowadzony w Pythonie 2.4 w oryginalnymsubprocess
module ( PEP-324 ). Jest to w zasadzie odpowiedniksubprocess.run(...).returncode
Interfejs API wysokiego poziomu vs.
subprocess.Popen()
Przebudowane i rozszerzone
subprocess.run()
są bardziej logiczne i wszechstronne niż starsze funkcje, które zastępuje. ZwracaCompletedProcess
obiekt, który ma różne metody, które pozwalają ci odzyskać status wyjścia, standardowe wyjście oraz kilka innych wyników i wskaźników statusu z ukończonego podprocesu.subprocess.run()
jest właściwą drogą, jeśli potrzebujesz tylko programu do uruchomienia i przywrócenia kontroli w Pythonie. W przypadku bardziej zaangażowanych scenariuszy (procesy w tle, być może z interaktywnymi wejściami / wyjściami z programem nadrzędnym Python) nadal musisz samodzielnie korzystaćsubprocess.Popen()
z całej instalacji hydraulicznej. Wymaga to dość skomplikowanego zrozumienia wszystkich ruchomych części i nie powinno być podejmowane pochopnie. ProstszyPopen
obiekt reprezentuje (prawdopodobnie nadal działający) proces, który musi być zarządzany z twojego kodu przez pozostałą część cyklu życia podprocesu.Należy chyba podkreślić, że po
subprocess.Popen()
prostu tworzy proces. Jeśli to zostawisz, masz podproces działający równolegle z Pythonem, więc proces „w tle”. Jeśli nie musi on wprowadzać danych wyjściowych ani danych wyjściowych ani w inny sposób koordynować z tobą, może wykonywać przydatne prace równolegle z programem Python.Unikaj
os.system()
ios.popen()
Od czasu wiecznego (dobrze, ponieważ Python 2.5) Do
os
dokumentacji moduł zawierał zalecenie preferowanysubprocess
przezos.system()
:Problem
system()
polega na tym, że jest to oczywiście zależne od systemu i nie oferuje sposobów interakcji z podprocesem. Po prostu działa, ze standardowym wyjściem i standardowym błędem poza zasięgiem Pythona. Jedyną informacją, którą Python otrzymuje z powrotem, jest status wyjścia polecenia (zero oznacza sukces, chociaż znaczenie niezerowych wartości jest również w pewnym stopniu zależne od systemu).PEP-324 (o którym już wspomniano powyżej) zawiera bardziej szczegółowe uzasadnienie, dlaczego
os.system
jest problematyczne i jaksubprocess
próbuje rozwiązać te problemy.os.popen()
był jeszcze bardziej zniechęcany :Jednak od pewnego czasu w Pythonie 3 został ponownie zaimplementowany, aby po prostu używać
subprocess
i przekierowuje dosubprocess.Popen()
dokumentacji w celu uzyskania szczegółowych informacji.Zrozum i zwykle używaj
check=True
Zauważysz również, że
subprocess.call()
ma wiele takich samych ograniczeń jakos.system()
. W regularnym użyciu powinieneś ogólnie sprawdzić, czy proces zakończył się pomyślnie, cosubprocess.check_call()
isubprocess.check_output()
zrobić (w przypadku gdy ten ostatni zwraca również standardowe wyjście gotowego podprocesu). Podobnie zwykle powinieneś używaćcheck=True
z,subprocess.run()
chyba że specjalnie musisz zezwolić podprocesowi na zwrócenie statusu błędu.W praktyce za pomocą
check=True
lubsubprocess.check_*
Python zgłaszaCalledProcessError
wyjątek, jeśli podproces zwraca niezerowy status wyjścia.Częstym błędem
subprocess.run()
jest pomijaniecheck=True
i zaskoczenie, gdy kod podrzędny zawiedzie, jeśli podproces nie powiedzie się.Z drugiej strony częstym problemem
check_call()
icheck_output()
było to, że użytkownicy, którzy ślepo korzystali z tych funkcji, byli zaskoczeni, gdy zgłoszono wyjątek, np. Gdygrep
nie znaleźli dopasowania. (Prawdopodobnie i tak powinieneś zastąpićgrep
natywnym kodem Python, jak opisano poniżej).Wszystko to się liczyło, musisz zrozumieć, w jaki sposób polecenia powłoki zwracają kod wyjścia i pod jakimi warunkami zwracają niezerowy (błąd) kod wyjścia, i podejmują świadomą decyzję, w jaki sposób należy go obsługiwać.
Zrozum i prawdopodobnie użyj
text=True
akauniversal_newlines=True
Od Python 3 ciągi znaków wewnętrznych w Pythonie są ciągami znaków Unicode. Ale nie ma gwarancji, że podproces generuje dane wyjściowe Unicode lub łańcuchy.
(Jeśli różnice nie są od razu oczywiste, zalecamy Pragmatic Unicode Neda Batcheldera , jeśli nie jest to absolutnie obowiązkowe, czytanie. Jeśli wolisz, za linkiem znajduje się 36-minutowa prezentacja wideo, chociaż samodzielne przeczytanie strony prawdopodobnie zajmie znacznie mniej czasu. )
W głębi duszy Python musi pobrać
bytes
bufor i jakoś go zinterpretować. Jeśli zawiera on kroplę danych binarnych, nie powinien być dekodowany na ciąg Unicode, ponieważ jest to podatne na błędy i powodujące błędy zachowanie - dokładnie takie nieznośne zachowanie, które zagadało wiele skryptów Python 2, zanim istniał sposób na właściwie rozróżnia zakodowany tekst i dane binarne.Za pomocą
text=True
mówisz Pythonowi, że w rzeczywistości oczekujesz danych tekstowych w domyślnym kodowaniu systemu i że należy je zdekodować do ciągu znaków w języku Python (Unicode), najlepiej jak to możliwe (zwykle UTF-8 na dowolnym, średnio do system dat, może z wyjątkiem Windowsa?)Jeśli nie o to prosisz, Python po prostu poda ci
bytes
łańcuchy w łańcuchachstdout
istderr
. Być może w niektórych późniejszym ty nie wiesz, że były one ciągi tekstowe po wszystkim, i znasz ich kodowanie. Następnie możesz je odkodować.W Pythonie 3.7 wprowadzono krótszy, bardziej opisowy i zrozumiały alias
text
argumentu słowa kluczowego, który wcześniej był nieco myląco nazywanyuniversal_newlines
.Zrozum
shell=True
vsshell=False
Gdy
shell=True
przekażesz pojedynczy ciąg do powłoki, a powłoka zabierze go stamtąd.Gdy
shell=False
przekażesz listę argumentów do systemu operacyjnego, omijając powłokę.Gdy nie masz powłoki, zapisujesz proces i pozbywasz się dość dużej ilości ukrytej złożoności, która może , ale nie musi zawierać błędy, a nawet problemy z bezpieczeństwem.
Z drugiej strony, gdy nie masz powłoki, nie masz przekierowania, rozszerzenia symboli wieloznacznych, kontroli zadań i wielu innych funkcji powłoki.
Częstym błędem jest używanie,
shell=True
a następnie przekazywanie Pythonowi listy tokenów lub odwrotnie. Zdarza się to w niektórych przypadkach, ale jest naprawdę źle zdefiniowane i może się zepsuć na ciekawe sposoby.Wspólna retorta „ale to działa dla mnie” nie jest użyteczną obaleniem, chyba że dokładnie zrozumiesz, w jakich okolicznościach może przestać działać.
Przykład refaktoryzacji
Bardzo często funkcje powłoki można zastąpić rodzimym kodem Python. Prosty Awk lub
sed
skrypty powinny raczej zostać po prostu przetłumaczone na Python.Aby częściowo to zilustrować, oto typowy, ale nieco głupi przykład, który obejmuje wiele funkcji powłoki.
Kilka rzeczy do zapamiętania tutaj:
shell=False
nie potrzebujesz cytowania, które powłoka wymaga wokół łańcuchów. W każdym razie wpisywanie cytatów jest prawdopodobnie błędem.Refaktoryzowany kod pokazuje również, jak bardzo powłoka naprawdę robi dla ciebie za pomocą bardzo zwięzłej składni - na lepsze lub gorsze. Python twierdzi, że jawne jest lepsze niż niejawne, ale kod w Pythonie jest dość szczegółowy i prawdopodobnie wygląda na bardziej złożony niż w rzeczywistości. Z drugiej strony oferuje szereg punktów, w których można przejąć kontrolę nad czymś innym, co w trywialny sposób ilustruje ulepszenie, które możemy łatwo dołączyć nazwę hosta wraz z wyjściem polecenia powłoki. (Nie jest to wcale trudne do wykonania w powłoce, ale kosztem kolejnej zmiany i być może innego procesu).
Typowe konstrukcje powłokowe
Dla kompletności, oto krótkie objaśnienia niektórych z tych funkcji powłoki oraz kilka uwag, w jaki sposób można je zastąpić rodzimymi funkcjami języka Python.
glob.glob()
prostymi ciągami Python, takimi jak bardzo częstofor file in os.listdir('.'): if not file.endswith('.png'): continue
. Bash ma różne inne funkcje rozszerzania, takie jak.{png,jpg}
rozwijanie nawiasów klamrowych, a{1..100}
także rozszerzanie tyldy (~
rozwija się do katalogu domowego, a bardziej ogólnie~account
do katalogu domowego innego użytkownika)$SHELL
lub$my_exported_var
czasami mogą być po prostu zastąpione zmiennymi Python. Wyeksportowane zmienne powłoki są dostępne jako np.os.environ['SHELL']
(Oznaczaexport
to, że zmienna jest dostępna dla podprocesów - zmienna, która nie jest dostępna dla podprocesów, oczywiście nie będzie dostępna dla Pythona działającego jako podproces powłoki lub odwrotnie. Słowoenv=
kluczowe argument dosubprocess
metod pozwala zdefiniować środowisko podprocesu jako słownik, więc jest to jeden ze sposobów, aby zmienna Python była widoczna dla podprocesu). Zshell=False
musisz zrozumieć, jak usunąć wszelkie cytaty; na przykładcd "$HOME"
odpowiadaos.chdir(os.environ['HOME'])
bez cudzysłowów wokół nazwy katalogu. (Bardzo częstocd
i tak nie jest użyteczne ani konieczne, a wielu początkujących pomija podwójne cudzysłowy wokół zmiennej i unika jej do pewnego dnia ... )grep 'foo' <inputfile >outputfile
otwiera sięoutputfile
do pisania iinputfile
do czytania oraz przekazuje jego zawartość jako standardowe wejście, dogrep
którego następnie trafia standardowe wyjścieoutputfile
. Zasadniczo nie jest to trudne do zastąpienia rodzimym kodem Python.echo foo | nl
uruchamia dwa podprocesy, w których standardowe wyjścieecho
jest standardowym wejściemnl
(na poziomie systemu operacyjnego, w systemach uniksopodobnych jest to uchwyt pojedynczego pliku). Jeśli nie możesz zastąpić jednego lub obu końców potoku rodzimym kodem Python, być może pomyśl o użyciu powłoki, zwłaszcza jeśli potok ma więcej niż dwa lub trzy procesy (spójrz napipes
moduł w standardowej bibliotece Python lub liczbę bardziej nowoczesnych i wszechstronnych konkurentów zewnętrznych).ls -l /
jest równoważna'ls' '-l' '/'
ale cytowania wokół literały jest całkowicie opcjonalne. Niecytowane łańcuchy zawierające metaznaki powłoki podlegają interpretacji parametrów, tokenizacji białych znaków i interpretacji symboli wieloznacznych; podwójne cudzysłowy zapobiegają tokenizacji białych znaków i interpretacji symboli wieloznacznych, ale umożliwiają rozszerzenie parametrów (podstawianie zmiennych, podstawianie poleceń i przetwarzanie odwrotnego ukośnika). Teoretycznie jest to proste, ale może być oszałamiające, szczególnie gdy istnieje kilka warstw interpretacji (na przykład polecenie zdalnej powłoki).Poznaj różnice między
sh
i Bashsubprocess
uruchamia polecenia powłoki,/bin/sh
chyba że wyraźnie zażądasz inaczej (z wyjątkiem oczywiście systemu Windows, w którym używa wartościCOMSPEC
zmiennej). Oznacza to, że różne funkcje dostępne tylko w Bash, takie jak tablice[[
itp., Nie są dostępne.Jeśli potrzebujesz użyć tylko składni Bash, możesz przekazać ścieżkę do powłoki jako
executable='/bin/bash'
(oczywiście jeśli Bash jest zainstalowany gdzie indziej, musisz dostosować ścieżkę).A
subprocess
jest oddzielne od swojego rodzica i nie może go zmienićNieco powszechnym błędem jest robienie czegoś takiego
który oprócz braku elegancji zdradza także fundamentalny brak zrozumienia „pod” części nazwy „podproces”.
Proces potomny działa całkowicie niezależnie od Pythona, a kiedy się kończy, Python nie ma pojęcia, co zrobił (oprócz niejasnych wskaźników, które może wywnioskować ze statusu wyjścia i wyniku procesu potomnego). Dziecko zasadniczo nie może zmienić środowiska rodzica; nie może ustawić zmiennej, zmienić katalogu roboczego lub, innymi słowy, komunikować się z rodzicem bez współpracy z rodzicem.
Natychmiastową poprawką w tym konkretnym przypadku jest uruchomienie obu poleceń w jednym podprocesie;
choć oczywiście ten szczególny przypadek użycia wcale nie wymaga powłoki. Pamiętaj, że możesz manipulować środowiskiem bieżącego procesu (a więc także jego elementów podrzędnych) za pośrednictwem
lub przekaż ustawienie środowiska procesowi potomnemu za pomocą
(nie wspominając o oczywistym refaktoryzacji
subprocess.run(['echo', 'bar'])
; aleecho
jest to kiepski przykład czegoś, co można uruchomić w podprocesie, oczywiście).Nie uruchamiaj Pythona z Pythona
To jest nieco wątpliwa rada; z pewnością istnieją sytuacje, w których ma to sens lub jest absolutnym wymogiem do uruchomienia interpretera Pythona jako podprocesu ze skryptu Python. Ale bardzo często poprawnym podejściem jest po prostu
import
inny moduł Python w skrypcie wywołującym i wywoływanie jego funkcji bezpośrednio.Jeśli drugi skrypt Pythona jest pod twoją kontrolą i nie jest to moduł, rozważ przekształcenie go w jeden . (Ta odpowiedź jest już za długa, więc nie będę tutaj zagłębiał się w szczegóły).
Jeśli potrzebujesz równoległości, możesz uruchamiać funkcje Pythona w podprocesach z
multiprocessing
modułem. Istnieje równieżthreading
funkcja, która uruchamia wiele zadań w jednym procesie (co jest lżejsze i daje większą kontrolę, ale także bardziej ograniczone, ponieważ wątki w procesie są ściśle powiązane i powiązane z jednym GIL .)źródło
run()
ślepo. Zaginionecheck=True
spowodował błąd, który można by uniknąć gdyby check_call użyto - „check” jest w nazwie, nie można stracić -jest prawidłowe domyślnie: nie ignoruj błędy cicho. Nie czytałem dalej.sh
ale pobiłeś mnie. Staram się opisać szczegóły wystarczająco szczegółowo, aby pomóc początkującym, dla których te pułapki nie są oczywiste, więc staje się to nieco opóźnione. W przeciwnym razie twoje powinno być wystarczające; +1stderr/stdout = subprocess.PIPE
wyższą wydajność niż ustawienia domyślne?os.system()
.Nazwij to podprocesem
Wydaje się, że pojawia się błąd, ponieważ na serwerze nie ma modułu wymiany, należy zainstalować swap na serwerze, a następnie ponownie uruchomić skrypt
źródło
swap
Moduł jest oczywiście tam, bo uruchamiając polecenie pracach powłoki.shell=True
, powinieneś użyć listy do przekazania wielu argumentów, tzn. użyj['a', 'b', 'c']
zamiast'a b c'
. Chociaż podział naiwny nie zadziała z powodu> file
(przekierowania powłoki) w poleceniu. Więcej szczegółówMożliwe jest użycie programu bash z parametrem -c do wykonania poleceń:
źródło
subprocess.check_output(bashCommand, shell=True)
robi to samo. Jeśli twoje polecenie jest ciągiem statycznym, spróbuj parsować go na liście i unikajshell=True
; chociaż w tym przypadku i tak potrzebujesz powłoki do przekierowania, albo będziesz musiałwith open('test.nt', 'w') as dest: output = subprocess.check_output(['cwm' ,'--rdf', 'test.rdf', '--ntriples'], stdout=dest, shell=False)
/bin/sh
(używane przez podproces) niekonieczniebash
(nie możesz używać bashism). Chociaż można użyć wexecutable='/bin/bash
razie potrzeby. Oto przykładowy kodcheck_output()
tutaj jest bezużyteczna (wynik jest zawsze pusty z powodu> file
przekierowania; użyjcheck_call()
zamiast tegoMożesz użyć
subprocess
, ale zawsze czułem, że nie był to „Python” sposób. Stworzyłem więc Sultan (bezwstydna wtyczka), która ułatwia uruchamianie funkcji wiersza poleceń.https://github.com/aeroxis/sultan
źródło
Zgodnie z błędem brakuje pakietu o nazwie swap na serwerze. To
/usr/bin/cwm
wymaga tego. Jeśli korzystasz z systemu Ubuntu / Debian, zainstalujpython-swap
za pomocą aptitude.źródło
swap
albo nie powinien go zaimportować. czy potrafiszimport swap
ręcznie Czy to działa?sys.path
tam, gdzie działa, a gdzie nie. Następnie spróbuj poszukać folderu wymiany lub swap.py w wydrukowanych folderach. Jak powiedział Sven, może istnieć problem z tymi ścieżkami, a to pomoże ci to rozgryźć.Możesz także użyć „os.popen”. Przykład:
Wynik:
źródło
subprocess
modułu”.os.popen
nie ma już tego ostrzeżenia, a teraz jest po prostu cienkim opakowaniemsubprocess.Popen()
.Aby uruchomić polecenie bez powłoki, przekaż polecenie jako listę i zaimplementuj przekierowanie w Pythonie, używając
[subprocess]
:Uwaga: nie
> test.nt
na końcu.stdout=file
realizuje przekierowanie.Aby uruchomić polecenie za pomocą powłoki w Pythonie, przekaż polecenie jako ciąg znaków i włącz
shell=True
:Oto powłoka jest odpowiedzialna za przekierowanie wyjścia (
> test.nt
znajduje się w poleceniu).Aby uruchomić polecenie bash korzystające z bashism, jawnie określ plik wykonywalny bash, np. W celu emulacji podstawienia procesu bash :
źródło
.split()
nie jest to odpowiednie, gdy występują cytowane ciągi itp. Istnieje osobna procedura,shlex.split()
która radzi sobie z dowolnie złożoną składnią powłoki..split()
działa w tym przypadku.shlex.split()
może być czasem użyteczny, ale w niektórych przypadkach może się nie powieść. Można wymienić wiele rzeczy. Możesz zacząć od linku do opisu znacznika podprocesu podanego powyżej.Pythonicznym sposobem na to jest użycie
subprocess.Popen
subprocess.Popen
pobiera listę, w której pierwszym elementem jest uruchamiane polecenie, a następnie dowolne argumenty wiersza polecenia.Jako przykład:
źródło
echo -v '"Hello Again!"'
z pojedynczymi cudzysłowami wokół podwójnych cudzysłowów.subprocesss.Popen
, musisz zarządzać wynikowym obiektem procesu (przynajmniej wykonaj a,wait()
aby zapobiec przekształceniu go w proces zombie).