Вопрос или проблема
У меня есть скрипт на Python test.py
, который пишет 100 строк, по одной в секунду:
from time import sleep
for i in range(100):
print(f'строка {i}')
sleep(1)
Я хотел бы перенаправить его вывод в файл log.txt
без буферизации, то есть чтобы каждая секунда добавлялась новая строка в файл.
Я пробовал:
python3 test.py > log.txt
python3 test.py | tee log.txt
stdbuf -oL python3 test.py > log.txt
script -c "python3 test.py" log.tx
Ни один из этих вариантов не записывает в log.txt
построчно во время выполнения test.py
. Весь вывод сбрасывается по окончании программы или прерывании. В чем решение?
Саму программу нужно изменить, чтобы она не буферизовала — или чтобы сбрасывала буферизованные данные чаще.
- Запустите python с опцией
-u
, - или используйте
sys.stdout.reconfigure(line_buffering=True)
, - или вызовите
print(..., flush=True)
, - или вызовите
sys.stdout.flush()
после каждого print.
Когда используется stdbuf -oL
, он вводит код, который перенастраивает Glibc ‘stdout’ аналогичным образом, но он только влияет на буферизацию Glibc FILE*
(т.е. с C-кода), а не на эквивалент Python.
Ответ или решение
Чтобы перенаправить стандартный вывод скрипта Python в файл без буферизации, важно понимать, как работает вывод в Python. По умолчанию стандартный вывод может быть буферизирован, что означает, что данные не будут немедленно записаны в файл, а будут накапливаться в памяти, пока буфер не заполнится или программа не завершится. Для вашего случая, где вы хотите, чтобы каждая строка записывалась в файл по мере её вывода, необходимо использовать один из следующих подходов.
Способы устранения буферизации стандартного вывода
-
Запуск Python с опцией
-u
:
Если вы запускаете ваш скрипт с ключом-u
, Python будет работать в небуферизованном режиме, что позволит ему записывать данные в файл сразу после их появления.python3 -u test.py > log.txt
-
Изменение конфигурации stdout:
Используйте методsys.stdout.reconfigure()
для установки линейной буферизации. Это можно сделать в самом скрипте. Добавьте следующие строки в начало вашего файлаtest.py
:import sys sys.stdout.reconfigure(line_buffering=True)
-
Использование параметра
flush
в функцииprint
:
Вы можете явно указать, чтобы данные выводились немедленно, добавив параметрflush=True
к функцииprint
:from time import sleep for i in range(100): print(f'line {i}', flush=True) sleep(1)
-
Вызов
sys.stdout.flush()
после каждогоprint
:
Вы также можете вручную вызывать методflush()
после каждого вызоваprint
, чтобы убедиться, что данные немедленно отправляются в файл. Это выглядит следующим образом:import sys from time import sleep for i in range(100): print(f'line {i}') sys.stdout.flush() sleep(1)
Примечание о других методах
Попытки использовать команды вроде stdbuf -oL
или script
не будут работать должным образом, когда речь идет о Python, поскольку они изменяют поведение буферизации в C-ориентированных системах вывода. Python имеет свои внутренние механизмы для управления буферизацией, что делает эти команды неэффективными.
Заключение
Для обеспечения немедленной записи каждой строки данных в файл log.txt
, вам нужно использовать один из описанных методов, который наиболее удобно вписывается в вашу рабочую среду. Опция -u
является наиболее быстрой и простой, в то время как изменения в коде могут дать большую гибкость и контроль над буферизацией.