Страницы

Поиск по вопросам

среда, 22 января 2020 г.

Как в bash-конвейере использовать вывод из python сразу?

#python #bash


Есть python скрипт script.py:
#!/usr/bin/env python
from time import sleep
print "Привет!"
sleep 10

Тогда если использовать его в bash-конвейере, то вывод происходит не сразу, а после
десяти секунд команды sleep.
Пример конвейера:
script.py | grep вет

Как сделать, чтобы вывод был сразу?
Есть вариант system('echo Привет!'). Но он не работает с русскими буквами. Может
его можно использовать?    


Ответы

Ответ 1



В этом коде вы столкнулись с буферизацией стандартного потока вывода (т.е. информация, переданная на поток вывода передаётся только тогда, когда её объём достаточно большой). Для того, чтобы в python 2.x оператор print (или функция print в python 3.x) очистила буфер вывода и передала информацию операционной системе, используется метод flush объекта sys.stdout: #!/usr/bin/env python # -*- coding: utf-8 -*- import sys import time print(u"Привет!") sys.stdout.flush() time.sleep(10) Пример: $ PYTHONIOENCODING=utf-8 your_script | grep вет Можно не добавлять flush() вызовы, а использовать -u опцию, которая выключает буферы у стандартных потоков ввода/вывода: $ PYTHONIOENCODING=utf-8 python -u your_script.py | grep вет PYTHONIOENCODING здесь используется для того, чтобы корректно указать кодировку, в которой осуществляется вывод программы. Она никак не связана с кодировкой исходного кода.

Ответ 2



Кратко: $ unbuffer ./script.py | grep pattern Если вы запустите скрипт, не перенаправляя его вывод в grep, то вывод должен появиться сразу не дожидаясь заполнения буфера: $ ./script.py Привет! Если стандартный вывод направлен в терминал (предполагается, что человек на него смотрит), то вывод буферизуется построчно (буфер очищается после каждой новой строки). Если вывод перенаправлен в pipe (потребляется другой программой—grep в данном случае), то буфер не очищается пока он целиком не заполнен (типичный размер 4КБ-8КБ)—возможно по соображениям производительности, чтобы уменьшить кол-во системных вызовов (write(2)). Поэтому вывод в примере не показан пока скрипт не завершился (когда все буфера очищаются при нормальном выходе). Картинка показывающая stdio буферы внутри процессов и pipe буфер в ядре для command1 | command2 команды: Это распространённое поведение, среди программ, использующих C stdio, таких как CPython реализация Питона 2. См. вторую причину в Q: Why not just use a pipe (popen())? Существует несколько способов очищать буфер или убрать буферизацию стандартного вывода вообще в Питоне: передать -u параметр командной строки: python -u .... Можно задать этот параметр в shebang: #!/usr/bin/python -u и запускать скрипт напрямую: ./script.py. установить PYTHONUNBUFFERED переменную окружения: $ PYTHONUNBUFFERED=nonempty python ... заменить sys.stdout на небуферизированный поток (например, с помощью os.fdopen()) передать flush=True параметр в print() функцию вызвать sys.stdout.flush() См. How to flush output of Python print? В отличии от Питона 2, на Питоне 3, текстовые потоки используют буфер для строк даже если -u передан—отличие может проявляться если вывод не содержит новой строки, например: import sys import time for i in range(10): print(i) sys.stdout.write('before sleep ') time.sleep(1) print('after sleep') Если передать -u, то числа печатаются сразу в обоих версиях (так как print(i) печатает новую строку). В Питоне 3, 'before sleep' задерживается до следующей итерации (когда следующая новая строка печатается), не смотря на -u параметр. Способы, требующие изменения исходного кода (манипуляция sys.stdout), могут быть хрупкими (вы можете забыть подставить flush=True в нужном месте) или вообще не работать (если вывод в неподконтрольном коде происходит) и они не всегда нужны. Часто лучше сконфигурировать окружение, в котором запускается скрипт, чтобы включить нужный режим буферизации, вместо редактирования кода. Если ваш скрипт запускает другие программы, которые могут печатать в стандартный вывод, то можно попробовать подменить C stdio буфер или с помощью pty заставить программу думать, что она выводит в терминал, а не pipe: $ unbuffer python ... $ stdbuf -oL -eL python ... $ script -q -c 'python ...' /dev/null | grep ... См. Turn off buffering in pipe.

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

Отправить комментарий