17

У меня есть долго работающий скрипт Python, который периодически выводит данные в стандартный вывод, который я вызывал с чем-то вроде:

python script.py > output.txt

Этот скрипт был запущен некоторое время, и я хочу остановить его с помощью Ctrl+C, но не потерять его вывод. К сожалению, когда я реализовал скрипт, я забыл очищать буфер после каждой строки вывода чем-то вроде sys.stdout.flush() (ранее предложенное решение для принудительной очистки вывода), поэтому вызов Ctrl+C прямо сейчас заставит меня потерять все мои выходные.

Если вам интересно, есть ли способ взаимодействия с работающим скриптом Python (или, в более общем случае, с запущенным процессом), чтобы заставить его очистить выходной буфер. Я не спрашиваю, как отредактировать и повторно запустить скрипт, чтобы заставить его корректно сбрасываться - этот вопрос конкретно касается взаимодействия с запущенным процессом (и, в моем случае, не потери вывода из текущего выполнения кода).

6 ответов6

16

Если кому-то действительно нужны эти данные, я бы предложил подключить отладчик gdb к интерпретатору python, на мгновение остановить задачу, вызвать fsync(1) (stdout), отсоединиться от нее (возобновить процесс) и перейти к просмотру выходного файла.

Посмотрите в /proc/$(pidof python)/fd чтобы увидеть допустимые файловые дескрипторы. $(pidof x) возвращает PID процесса с именем ' x '.

# your python script is running merrily over there.... with some PID you've determined.
#
# load gdb
gdb
#
# attach to python interpreter (use the number returned by $(pidof python))
attach 1234
#
# force a sync within the program's world (1 = stdout, which is redirected in your example)
call fsync(1)
#
# the call SHOULD have returned 0x0, sync successful.   If you get 0xffffffff (-1), perhaps that wasn't stdout.  0=stdin, 1=stdout, 2=stderr
#
# remove our claws from poor python
detach
#
# we're done!
quit

Я использовал этот метод, чтобы изменить рабочий каталог, настроить параметры на лету ... многие вещи. Увы, вы можете вызывать только те функции, которые определены в работающей программе, однако fsync работает хорошо.

(Команда gdb ' info functions ' выведет список всех доступных функций. Будьте осторожны, хотя. Вы работаете в режиме реального времени .)

Существует также команда peekfd (находится в пакете psmisc в Debian Jessie и других), которая позволит вам увидеть, что скрывается в буферах процесса. Опять же, /proc/$(pidof python)/fd покажет вам допустимые файловые дескрипторы, которые нужно передать в качестве аргументов peekfd.

Если вы не помните -u для python, вы всегда можете поставить перед командой команду stdbuf (в уже установленном coreutils ), чтобы установить для stdin/stdout/stderr значение unbuffered, строку с буферизацией или блок с буферизацией по желанию:

stdbuf -i 0 -o 0 -e 0 python myscript.py > unbuffered.output

Конечно, man pages - твои друзья, эй! возможно, псевдоним может быть полезным и здесь.

alias python='python -u'

Теперь ваш питон всегда использует -u для всех ваших попыток командной строки!

5

Сначала убедитесь, что у вас есть символы отладки для Python (или хотя бы glibc). На Fedora 1 вы можете установить их с помощью:

dnf debuginfo-install python

Затем присоедините gdb к запущенному сценарию и выполните следующие команды:

[user@host ~]$ pidof python2
9219
[user@host ~]$ gdb python2 9219
GNU gdb (GDB) Fedora 7.7.1-13.fc20
...
0x00007fa934278780 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:81
81  T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)
(gdb) call fflush(stdout)
$1 = 0
(gdb) call setvbuf(stdout, 0, 2, 0)
$2 = 0
(gdb) quit
A debugging session is active.

    Inferior 1 [process 9219] will be detached.

Quit anyway? (y or n) y
Detaching from program: /usr/bin/python2, process 9219

Это очистит стандартный вывод, а также отключит буферизацию. 2 из вызова setvbuf - это значение _IONBF в моей системе. Вам нужно выяснить, что у вас (grep _IONBF /usr/include/stdio.h должен помочь).

Основываясь на том, что я видел в реализации PyFile_SetBufSize и PyFile_WriteString в CPython 2.7, он должен работать довольно хорошо, но я не могу дать никаких гарантий.


1 Fedora включает в себя специальный тип RPM, называемый debuginfo rpms. Эти автоматически созданные RPM содержат информацию об отладке из программных файлов, но перемещены во внешний файл.

4

Там нет решения вашей непосредственной проблемы. Если ваш скрипт уже запущен, вы не можете изменить режим буферизации по факту. Это все буферы в памяти, и все это настраивается при запуске сценария, открытии файловых дескрипторов, создании каналов и т.д.

В общем, если и только если некоторая или вся рассматриваемая буферизация выполняется на уровне ввода-вывода на выходе, вы можете выполнить команду sync ; но это, как правило, маловероятно в таком случае.

В будущем вы можете использовать параметр Python -u * для запуска скрипта. В целом, многие команды имеют специфичные для команды опции для отключения буферизации stdin/stdout, и вы также можете добиться общего успеха с командой unbuffer из expect пакета.

Ctrl+C приведет к сбросу буферов системного уровня, когда программа прерывается, если только буферизация не выполняется самим Python, и он не реализовал логику сброса собственных буферов с помощью Ctrl+C. Приостановка, сбой или убийство не будут такими добрыми.

* Принудительно отключить буферизацию stdin, stdout и stderr.

2

Похоже, я был слишком осторожен с потерей буферизованного вывода после выполнения Ctrl-C; в соответствии с этим постом я должен ожидать, что буфер будет очищен, если моя программа имеет нормальный выход, что было бы, если бы я нажал Ctrl-C. С другой стороны, я бы потерял буферизованный вывод, если бы убил скрипт с помощью SIGKILL или подобного.

2

Python 2.7.7 Документация, раздел "Настройка и использование Python", подраздел 1.Командная строка и окружение, описывает этот аргумент Python:

-u

Заставьте stdin, stdout и stderr быть полностью небуферизованными. В системах, где это важно, также установите stdin, stdout и stderr в двоичном режиме.

Обратите внимание, что в file.readlines() и File Objects (для строки в sys.stdin) есть внутренняя буферизация, на которую не влияет эта опция. Чтобы обойти это, вы захотите использовать file.readline () внутри цикла while 1:.

А также эта переменная среды:

PYTHONUNBUFFERED

Если для этого параметра задана непустая строка, это эквивалентно указанию опции -u.

0

Я думаю, что другое возможное решение может заключаться в том, чтобы принудительно завершить процесс с выгруженным ядром, а затем посмертно анализировать содержимое памяти.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .