Jak przekierować standardowe wyjście do dowolnego pliku w Pythonie?
Kiedy długo działający skrypt Pythona (np. Aplikacja internetowa) zostanie uruchomiony z sesji ssh i zostanie zablokowany, a sesja ssh zostanie zamknięta, aplikacja podniesie IOError i zakończy się niepowodzeniem w momencie próby zapisu na standardowe wyjście. Musiałem znaleźć sposób, aby aplikacja i moduły były wyprowadzane do pliku, a nie standardowe, aby zapobiec awarii z powodu IOError. Obecnie używam nohup do przekierowywania danych wyjściowych do pliku, i to robi zadanie, ale zastanawiałem się, czy istnieje sposób, aby to zrobić bez użycia nohup, z ciekawości.
Próbowałem już sys.stdout = open('somefile', 'w')
, ale nie wydaje się, aby niektóre zewnętrzne moduły nadal wysyłały dane do terminala (a może sys.stdout = ...
linia w ogóle nie zadziałała). Wiem, że powinien działać z prostszych skryptów, na których testowałem, ale nie miałem jeszcze czasu na testowanie w aplikacji internetowej.
script.p > file
someprocess | python script.py
? Po co angażowaćnohup
?print
instrukcje, aby zastosowaćlogging
moduł ze stdlib. Następnie możesz przekierować dane wyjściowe wszędzie, mieć kontrolę nad tym, ile chcesz danych wyjściowych itp. W większości przypadków kod produkcyjny nie powinien,print
alelog
.Odpowiedzi:
Jeśli chcesz dokonać przekierowania w skrypcie Python, ustawienie
sys.stdout
obiektu obiektu załatwi sprawę:O wiele bardziej powszechną metodą jest użycie przekierowania powłoki podczas wykonywania (to samo w systemach Windows i Linux):
źródło
from sys import stdout
, może dlatego, że tworzy lokalną kopię. Możesz także używać gowith
npwith open('file', 'w') as sys.stdout: functionThatPrints()
. Z Możesz teraz wdrożyćfunctionThatPrints()
za pomocą normalnychprint
instrukcji.stdout = sys.stdout
dzięki czemu można umieścić go z powrotem kiedy skończysz,sys.stdout = stdout
. W ten sposób, jeśli jesteś wywoływany z funkcji, która używaprint
, nie spieprzysz ich.buffering=0
wyłącza buforowanie (może to negatywnie wpłynąć na wydajność (10-100 razy)).buffering=1
włącza buforowanie linii, dzięki czemu można użyćtail -f
do wyjścia zorientowanego liniowo.sys.stdout = sys.__stdout__
aby go odzyskać.W Pythonie 3.4 jest
contextlib.redirect_stdout()
funkcja :To jest podobne do:
które mogą być używane we wcześniejszych wersjach Pythona. Ta ostatnia wersja nie nadaje się do ponownego użycia . W razie potrzeby można go utworzyć.
Nie przekierowuje standardowego wyjścia na poziomie deskryptorów plików, np .:
b'not redirected'
i'echo this also is not redirected'
nie są przekierowywani dooutput.txt
pliku.Aby przekierować na poziomie deskryptora pliku,
os.dup2()
można użyć:Ten sam przykład działa teraz, jeśli
stdout_redirected()
zostanie użyty zamiastredirect_stdout()
:Wyjście, które poprzednio było drukowane na standardowym wyjściu, przechodzi teraz
output.txt
tak długo, jak długostdout_redirected()
aktywny jest menedżer kontekstu.Uwaga:
stdout.flush()
nie opróżnia buforów C stdio w Pythonie 3, w którym operacje we / wy są implementowane bezpośrednio w wywołaniachread()
/write()
systemowych. Aby opróżnić wszystkie otwarte strumienie wyjściowe C stdio, możesz wywołaćlibc.fflush(None)
jawnie, jeśli niektóre rozszerzenia C używają I / O opartych na stdio:Możesz użyć
stdout
parametru do przekierowania innych strumieni, nie tylkosys.stdout
np. Do scaleniasys.stderr
isys.stdout
:Przykład:
Uwaga:
stdout_redirected()
miksuje buforowanesys.stdout
operacje we / wy ( zwykle) i niebuforowane operacje we / wy (operacje na deskryptorach plików bezpośrednio). Uwaga, mogą wystąpić problemy z buforowaniem .Aby odpowiedzieć, edytuj: możesz użyć
python-daemon
do demonizacji skryptu i użyćlogging
modułu (jak sugerowano @ erikb85 ) zamiastprint
instrukcji i po prostu przekierować standardowe wyjście dla długoterminowego skryptu Python, którego używasznohup
teraz.źródło
stdout_redirected
jest pomocny. Pamiętaj, że to nie działa w ramach doctests, ponieważ specjalnySpoofOut
program obsługi doctest, który zastępujesys.stdout
, nie mafileno
atrybutu.ValueError("Expected a file (`.fileno()`) or a file descriptor")
, oznacza to błąd. Jesteś pewien, że to go nie podnosi?doctest.sys.__stdout__
gdzie normalnie byśmy korzystalisys.stdout
. To nie jest problem z twoją funkcją, tylko zakwaterowanie wymagane dla doctest, ponieważ zastępuje stdout obiektem, który nie ma wszystkich atrybutów, jakie miałby prawdziwy plik.stdout_redirected()
mastdout
parametr, możesz ustawić go na,sys.__stdout__
jeśli chcesz przekierować oryginalny stdout python (który powinien mieć poprawny.fileno()
w większości przypadków). Nie robi nic dla prądu,sys.stdout
jeśli są różne. Nie używajdoctest.sys
; jest dostępny przez przypadek.with stdout_redirected(to=fd):
with merged_stderr_stdout():
print('...'); print('...', file=sys.stderr)
możesz spróbować tego o wiele lepiej
źródło
logger
lubsyslog
?def __getattr__(self, attr): return getattr(self.terminal, attr)
def flush(self):
metodę do klasyLogger
.Pozostałe odpowiedzi nie obejmowały przypadku, w którym chcesz, aby rozwidlone procesy współużytkowały Twój nowy standard.
Aby to zrobić:
źródło
sys.stdout.flush()
przedclose(1)
instrukcją, aby upewnić się, że'file'
plik przekierowania otrzymuje dane wyjściowe. Możesz także użyćtempfile.mkstemp()
pliku zamiast'file'
. I bądź ostrożny, nie masz uruchomionych innych wątków, które mogłyby ukraść pierwszy uchwyt pliku systemu operacyjnego po,os.close(1)
ale przed'file'
otwarciem, aby użyć uchwytu.os.dup2()
i zawinąć go do menedżera kontekstu, jak pokazano w mojej odpowiedziCytat z PEP 343 - Instrukcja „z” (dodano instrukcję importu):
Tymczasowo przekieruj standardowe wyjście:
Używany w następujący sposób:
Oczywiście nie jest to bezpieczne dla wątków, ale nie wykonuje tego samego tańca ręcznie. W programach jednowątkowych (na przykład w skryptach) jest to popularny sposób robienia rzeczy.
źródło
os.system('echo not redirected')
. Moja odpowiedź pokazuje, jak przekierować takie wyjścieredirect_stdout
wcontextlib
źródło
Oto wariant odpowiedzi Yudy Prawiry :
flush()
i wszystkie atrybuty plikustderr
również.
źródło
W oparciu o tę odpowiedź: https://stackoverflow.com/a/5916874/1060344 , oto inny sposób, w jaki zorientowałem się, którego używam w jednym z moich projektów. Niezależnie od tego, co zastąpisz
sys.stderr
lubsys.stdout
czym będziesz musiał, upewnij się, że zamiana jest zgodna zfile
interfejsem, szczególnie jeśli robisz to, ponieważ stderr / stdout są używane w innej bibliotece, która nie jest pod twoją kontrolą. Ta biblioteka może używać innych metod obiektu pliku.Sprawdź ten sposób, w którym wciąż pozwalam, aby wszystko poszło na stderr / stdout (lub dowolny inny plik w tym przypadku), a także wysłać wiadomość do pliku dziennika za pomocą funkcji rejestrowania Pythona (ale naprawdę możesz to zrobić za pomocą tego):
źródło
Potrzebujesz multipleksera terminala, takiego jak ekran Tmux lub GNU
Dziwi mnie, że niewielki komentarz Ryana Amosa do pierwotnego pytania jest jedyną wzmianką o rozwiązaniu zdecydowanie lepszym od wszystkich pozostałych w ofercie, bez względu na to, jak sprytne może być podstępne pytanie i ile otrzymali głosów poparcia. W nawiązaniu do komentarza Ryana tmux jest dobrą alternatywą dla ekranu GNU.
Ale zasada jest taka sama: jeśli kiedykolwiek wylogujesz się z pracy terminalu, udaj się do kawiarni na kanapkę, pop w łazience, idź do domu (itp.), A następnie ponownie połącz się z sesja z dowolnego terminala lub dowolnym komputerze tak, jakbyś nigdy nie był daleko, multipleksery zaciskowe są odpowiedź. Pomyśl o nich jak o VNC lub zdalnym pulpicie dla sesji terminalowych. Wszystko inne jest obejściem. Jako bonus, gdy pojawi się szef i / lub partner i przypadkowo ctrl-w / cmd-w okno terminalu zamiast okna przeglądarki z jego podejrzaną zawartością, nie stracisz ostatnich 18 godzin przetwarzania !
źródło
Programy napisane w innych językach (np. C) muszą wykonywać specjalną magię (zwaną podwójnym rozwidleniem), aby odłączyć się od terminala (i zapobiec procesom zombie). Myślę więc, że najlepszym rozwiązaniem jest ich emulacja.
Zaletą ponownego uruchamiania programu jest to, że możesz wybrać przekierowania w wierszu poleceń, np
/usr/bin/python mycoolscript.py 2>&1 1>/dev/null
Zobacz ten post, aby uzyskać więcej informacji: Jaki jest powód wykonania podwójnego rozwidlenia podczas tworzenia demona?
źródło
systemd
,upstart
) lub innego narzędzia (daemon(1)
) do obsługi rozwidlonej płyty kotłowej.