Podczas próby zapisania standardowego skryptu ze skryptu w języku Python do pliku tekstowego ( python script.py > log
) plik tekstowy jest tworzony po uruchomieniu polecenia, ale rzeczywista zawartość nie jest zapisywana, dopóki skrypt się nie zakończy. Na przykład:
script.py:
import time
for i in range(10):
print('bla')
time.sleep(5)
wypisuje na standardowe wyjście co 5 sekund po wywołaniu python script.py
, ale kiedy dzwonię python script.py > log
, rozmiar pliku dziennika pozostaje zerowy do momentu zakończenia skryptu. Czy jest możliwe bezpośrednie zapisanie do pliku dziennika, abyś mógł śledzić postęp skryptu (np. Używając tail
)?
EDYCJA Okazuje się, że to python -u script.py
działa, nie wiedziałem o buforowaniu standardowego wyjścia.
Odpowiedzi:
Dzieje się tak, ponieważ normalnie, gdy proces STDOUT jest przekierowywany na coś innego niż terminal, wówczas dane wyjściowe są buforowane w buforze o rozmiarze specyficznym dla systemu operacyjnego (być może 4k lub 8k w wielu przypadkach). I odwrotnie, podczas wysyłania do terminala STDOUT będzie buforowany liniowo lub nie będzie buforowany, więc zobaczysz wynik po
\n
każdym znaku lub dla każdego znaku.Ogólnie można zmienić buforowanie STDOUT za pomocą
stdbuf
narzędzia:Teraz, jeśli
tail -F log
powinieneś, powinieneś zobaczyć wyjście każdego wiersza natychmiast po jego wygenerowaniu.Alternatywnie jawne opróżnianie strumienia wyjściowego po każdym wydruku powinno osiągnąć to samo. Wygląda na to, że
sys.stdout.flush()
powinno to zostać osiągnięte w Pythonie. Jeśli używasz Pythona 3.3 lub nowszy,print
funkcja ma równieżflush
słowa kluczowego, które wykonuje to:print('hello', flush=True)
.źródło
python -u script.py
to załatwia sprawę. EDYTUJ Tak wiele odpowiedzi na raz, zaakceptowałem twoją, ponieważ wskazała mi kierunek buforowania.--line-buffered
Dlagrep
, ale inne nie.stdbuf
jest ogólnym narzędziem catchall do radzenia sobie z tymi, które tego nie robią.stdbuf -o0 python script.py > log
W takich określonych okolicznościach?-oL
to kompromis. Zasadniczo większe bufory zapewnią lepszą wydajność podczas przekierowania (mniej wywołań systemowych i mniej operacji we / wy). Jeśli jednak bezwzględnie konieczne jest, aby zobaczyć każdy znak w postaci wyjściowej, wówczas tak,-o0
byłoby wymagane.To powinno wykonać zadanie:
Ponieważ Python buforuje
stdout
domyślnie, tutaj użyłemsys.stdout.flush()
do opróżnienia bufora.Innym rozwiązaniem byłoby użycie
-u
(niebuforowanego) przełącznikapython
. Tak więc zrobią to również:źródło
Wariacją na temat używania własnej opcji Pythona dla niebuforowanych danych wyjściowych byłoby użycie
#!/usr/bin/python -u
jako pierwszego wiersza.Z
#!/usr/bin/env python
tym dodatkowym argumentem nie zadziała, więc alternatywnie można uruchomićPYTHONUNBUFFERED=1 ./my_scriipt.py > output.txt
lub zrobić to w dwóch krokach:źródło
Powinieneś przejść
flush=True
doprint
funkcji:Zgodnie z dokumentacją domyślnie
print
nie wymusza niczego na opróżnianie:Dokumentacja
sys
strumieni mówi:Jeśli utkniesz w starożytnej wersji Pythona, musisz wywołać
flush
metodęsys.stdout
strumienia:źródło
DigitalTrauma
powiedziane 10 godzin wcześniej. Powinieneś zagłosować za jego postem, a nie znowu to samo.print(flush=True)
została dodana do tej odpowiedzi po mojej przez autora zewnętrznego. Uważam, że to zły gust, aby zrywać treści z mojej odpowiedzi i umieszczać je w innym, bez uznania. Postanowiłem dodać moją odpowiedź wyłącznie dlatego, że żadne odpowiedzi nie zawierały wzmianki o najprostszym sposobie osiągnięcia tego, czego OP chciał w nowszych wersjach Pythona, i dodałem „starą drogę” dla kompletności. Następnym razem sprawdź historię zmian przed komentowaniem i / lub oddaniem głosu.__future__
import:from __future__ import print_function
. Ale tak, to tylko dla zgodności z Pythonem 3