Почему стандартный вывод из подпроцесса (перенаправленный в небуферизованный файл) буферизуется?

#python #subprocess #stdout #popen #buffering

#python #подпроцесс #стандартный вывод #popen #буферизация

Вопрос:

Из http://docs.python.org/library/functions.html#open

Необязательный аргумент bufsize указывает желаемый размер буфера файла: 0 означает небуферизованный, 1 означает буферизованный по строке, любое другое положительное значение означает использование буфера (приблизительно) такого размера. Отрицательный размер буфера означает использование системного значения по умолчанию, которое обычно буферизуется по строкам для устройств tty и полностью буферизуется для других файлов. Если опущено, используется системное значение по умолчанию.

Я передаю 0 как bufsize ниже, но без использования flush() при запуске main_process вывод не записывается в файл.
В чем причина?

 # --------------------------------- sub_process.py
import sys
import time

if __name__ == '__main__':
    print 'printed from redirect.py'
    # why is the following flush() needed? 'std-output' is (?) unbuffered...
    sys.stdout.flush() 
    time.sleep(6)


# --------------------------------- main_process.py
import subprocess
import time

if __name__ == '__main__':
    p = subprocess.Popen(
        ['python', 'sub_process.py'],
        stdout=open('std-output', 'w', 0))
    time.sleep(3)
    p.terminate()
  

Комментарии:

1. 1, я потратил около 30 минут, пытаясь выяснить, почему sys.stdout -> subprocess.PIPE не работал пару дней назад. flush() это ответ, но зачем он нам нужен???

Ответ №1:

Используйте python с флагом -u, например:

 if __name__ == '__main__':
    p = subprocess.Popen(
        ['python', '-u', 'sub_process.py'],
        stdout=open('std-output', 'w'))
    time.sleep(3)
    p.terminate()
  

Комментарии:

1. Это работает, спасибо. Хотя мне все еще интересно, зачем это вообще нужно?

2. Я предполагаю, что если вы записываете непосредственно в файл с помощью file. write () это будет сделано без буферизации, но фактически вы записываете в него косвенно, поскольку python будет буферизовать ваши команды печати перед отправкой данных в ваш файл, следовательно, вам нужен флаг -u, чтобы указать python не буферизовать команды печати.

3. sys.stdout.write('printed from redirect.py') выдает тот же результат, что и print — в файл ничего не записывается

Ответ №2:

Расширение решения Magnus Skog ( 1, кстати :)):

Ну, в основном, что происходит, когда подпроцесс разветвляет новый процесс, он дублирует аргумент stdout для нового стандартного вывода подпроцесса (fileno = 1) с использованием os.dup2 (посмотрите на подпроцесс.Popen._execute_child) и это сохранит небуферизованное состояние (как это делает dup2), до сих пор все было хорошо, но когда python будет запущен (в подпроцессе), по умолчанию, если python не видит флага -u, он установит буфер стандартного вывода в буфер строк (взгляните на основную функцию python.) который переопределит флаг буферизации, который вы установили ранее.

Надеюсь, это больше объясняет поведение, которое вы наблюдали.