Как перенаправить стандартный вывод в файл без буферизации?

Вопрос или проблема

У меня есть скрипт на 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. По умолчанию стандартный вывод может быть буферизирован, что означает, что данные не будут немедленно записаны в файл, а будут накапливаться в памяти, пока буфер не заполнится или программа не завершится. Для вашего случая, где вы хотите, чтобы каждая строка записывалась в файл по мере её вывода, необходимо использовать один из следующих подходов.

Способы устранения буферизации стандартного вывода

  1. Запуск Python с опцией -u:
    Если вы запускаете ваш скрипт с ключом -u, Python будет работать в небуферизованном режиме, что позволит ему записывать данные в файл сразу после их появления.

    python3 -u test.py > log.txt
  2. Изменение конфигурации stdout:
    Используйте метод sys.stdout.reconfigure() для установки линейной буферизации. Это можно сделать в самом скрипте. Добавьте следующие строки в начало вашего файла test.py:

    import sys
    sys.stdout.reconfigure(line_buffering=True)
  3. Использование параметра flush в функции print:
    Вы можете явно указать, чтобы данные выводились немедленно, добавив параметр flush=True к функции print:

    from time import sleep
    
    for i in range(100):
       print(f'line {i}', flush=True)
       sleep(1)
  4. Вызов 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 является наиболее быстрой и простой, в то время как изменения в коде могут дать большую гибкость и контроль над буферизацией.

Оцените материал
Добавить комментарий

Капча загружается...