Как перенаправить стандартный вывод в произвольный файл в Python?
Когда длительно работающий скрипт Python (например, веб-приложение) запускается изнутри сеанса ssh и отключается, а сеанс ssh закрывается, приложение вызывает IOError и завершает работу в тот момент, когда оно пытается записать в stdout. Мне нужно было найти способ, чтобы приложение и модули выводили в файл, а не в stdout, чтобы предотвратить сбой из-за IOError. В настоящее время я использую nohup для перенаправления вывода в файл, и это делает работу, но мне было интересно, есть ли способ сделать это без использования nohup, из любопытства.
Я уже пробовал sys.stdout = open('somefile', 'w')
, но это не мешает некоторым внешним модулям по-прежнему выводить на терминал (или, возможно, sys.stdout = ...
линия вообще не сработала). Я знаю, что он должен работать с более простыми скриптами, на которых я тестировал, но у меня еще не было времени для тестирования веб-приложения.
script.p > file
someprocess | python script.py
? Зачем привлекатьnohup
?print
операторы, чтобы применитьlogging
модуль из stdlib. После этого вы можете перенаправить вывод везде, есть контроль над тем, сколько выходом вы хотите и т.д. В большинстве случаев кода производства не должен ,print
ноlog
.Ответы:
Если вы хотите сделать перенаправление внутри скрипта Python, установка
sys.stdout
на объект файла делает свое дело:Гораздо более распространенным методом является использование перенаправления оболочки при выполнении (то же самое в Windows и Linux):
источник
from sys import stdout
, возможно, потому что это создает локальную копию. Также вы можете использовать егоwith
, например, сwith open('file', 'w') as sys.stdout: functionThatPrints()
. Теперь вы можете реализоватьfunctionThatPrints()
с помощью обычныхprint
операторов.stdout = sys.stdout
чтобы вы могли положить ее обратно, когда закончитеsys.stdout = stdout
. Таким образом, если вы вызываетесь из функции, которая использует,print
вы не облажаетесь.buffering=0
отключает буферизацию (это может негативно повлиять на производительность (10-100 раз)).buffering=1
разрешает буферизацию строки, чтобы вы могли использовать ееtail -f
для линейно-ориентированного вывода.sys.stdout = sys.__stdout__
чтобы получить его обратно.В Python 3.4 есть
contextlib.redirect_stdout()
функция :Это похоже на:
это можно использовать в более ранних версиях Python. Последняя версия не подлежит повторному использованию . Это может быть сделано один при желании.
Он не перенаправляет стандартный вывод на уровне файловых дескрипторов, например:
b'not redirected'
и'echo this also is not redirected'
не перенаправляются вoutput.txt
файл.Чтобы перенаправить на уровне дескриптора файла,
os.dup2()
можно использовать:Тот же самый пример работает теперь, если
stdout_redirected()
используется вместоredirect_stdout()
:Вывод, который ранее был напечатан на stdout, теперь идет в
output.txt
тех пор,stdout_redirected()
пока активен менеджер контекста.Примечание:
stdout.flush()
не очищает буферы C stdio на Python 3, где ввод / вывод реализован непосредственно при вызовахread()
/write()
system. Чтобы очистить все открытые потоки вывода C stdio, вы можетеlibc.fflush(None)
явно вызвать, если какое-то расширение C использует ввод / вывод на основе stdio:Вы можете использовать
stdout
параметр для перенаправления других потоков, не только,sys.stdout
например, для объединенияsys.stderr
иsys.stdout
:Пример:
Примечание:
stdout_redirected()
смешивает буферизованный ввод / вывод (sys.stdout
обычно) и небуферизованный ввод / вывод (операции с дескрипторами файлов напрямую). Осторожно, могут быть проблемы с буферизацией .Чтобы ответить, отредактируйте: вы можете использовать,
python-daemon
чтобы демонизировать ваш скрипт и использоватьlogging
модуль (как предложено @ erikb85 ) вместоprint
операторов и просто перенаправить stdout для вашего долгосрочного скрипта Python, который выnohup
сейчас используете .источник
stdout_redirected
полезно Имейте в виду, что это не работает внутри doctest, так как специальныйSpoofOut
обработчик doctest, используемый для заменыsys.stdout
, не имеетfileno
атрибута.ValueError("Expected a file (`.fileno()`) or a file descriptor")
то это ошибка. Вы уверены, что это не поднимает это?doctest.sys.__stdout__
где мы обычно будем использоватьsys.stdout
. Это не проблема с вашей функцией, просто приспособление, необходимое для doctest, так как он заменяет стандартный вывод объектом, который не имеет всех атрибутов, которые имел бы истинный файл.stdout_redirected()
Имеетstdout
параметр, вы можете установить его,sys.__stdout__
если хотите перенаправить исходный стандартный вывод Python (.fileno()
в большинстве случаев он должен быть действительным ). Он ничего не делает для тока,sys.stdout
если они разные. Не используйтеdoctest.sys
; это доступно случайно.with stdout_redirected(to=fd):
with merged_stderr_stdout():
print('...'); print('...', file=sys.stderr)
ты можешь попробовать это намного лучше
источник
logger
илиsyslog
?def __getattr__(self, attr): return getattr(self.terminal, attr)
def flush(self):
метод в классLogger
.Другие ответы не охватывали случай, когда вы хотите, чтобы разветвленные процессы использовали ваш новый стандартный вывод.
Для этого:
источник
sys.stdout.flush()
before,close(1)
чтобы убедиться, что'file'
файл перенаправления получает выходные данные. Кроме того, вы можете использоватьtempfile.mkstemp()
файл вместо'file'
. И будьте осторожны, у вас не запущены другие потоки, которые могут украсть первый дескриптор файла ОС после,os.close(1)
но до'file'
открытия, чтобы использовать дескриптор.os.dup2()
и обернуть в контекстный менеджер, как показано в моем ответеЦитируется из PEP 343 - Заявление «с» (добавлен оператор импорта):
Временно перенаправить стандартный вывод:
Используется следующим образом:
Конечно, это не потокобезопасно, но ни один из них не выполняет этот же танец вручную. В однопоточных программах (например, в скриптах) это популярный способ работы.
источник
os.system('echo not redirected')
. Мой ответ показывает, как перенаправить такой выводredirect_stdout
вcontextlib
источник
Вот вариант ответа Юда Правира :
flush()
и все атрибуты файлаstderr
также,
источник
Основываясь на этом ответе: https://stackoverflow.com/a/5916874/1060344 , вот еще один способ, который я выяснил, который я использую в одном из моих проектов. Что бы вы ни заменяли
sys.stderr
или чем бы то ни былоsys.stdout
, вы должны убедиться, что замена соответствуетfile
интерфейсу, особенно если вы этим занимаетесь, потому что stderr / stdout используются в какой-то другой библиотеке, которая не находится под вашим контролем. Эта библиотека может использовать другие методы объекта файла.Посмотрите на этот способ, где я все еще позволяю всему делать stderr / stdout (или любой файл в этом отношении), а также отправлять сообщение в файл журнала с помощью средства ведения журнала Python (но вы действительно можете сделать что-нибудь с этим):
источник
Вам нужен терминальный мультиплексор, такой как tmux или экран GNU
Я удивлен тем, что небольшой комментарий Райана Амоса к первоначальному вопросу - единственное упоминание о решении, гораздо более предпочтительном, чем все остальные, предлагаемые, независимо от того, насколько хитрым может быть обман с питоном и сколько голосов они получили. В дополнение к комментарию Райана, tmux - хорошая альтернатива экрану GNU.
Но принцип тот же: если вам захочется оставить работу терминала во время выхода из системы, отправляйтесь в кафе за сэндвичем, загляните в ванную, идите домой (и т. Д.), А затем снова подключитесь к терминальная сессия из любого места или любого компьютера , как если бы вы никогда не были далеки, терминальные мультиплексоры ответ. Думайте о них как о VNC или удаленном рабочем столе для терминальных сессий. Все остальное - обходной путь. В качестве бонуса, когда к вам придет босс и / или партнер и вы по неосторожности нажмете ctrl-w / cmd-w в окне терминала, а не в окне браузера с его хитрым контентом, вы не потеряете обработку за последние 18 часов. !
источник
Программы, написанные на других языках (например, C), должны выполнять специальную магию (называемую двойным разветвлением), чтобы отсоединиться от терминала (и предотвратить процессы зомби). Поэтому я думаю, что лучшим решением будет подражать им.
Плюс повторного выполнения вашей программы, вы можете выбрать перенаправления в командной строке, например
/usr/bin/python mycoolscript.py 2>&1 1>/dev/null
См. Этот пост для получения дополнительной информации: Какова причина выполнения двойной вилки при создании демона?
источник
systemd
,upstart
) или другую утилиту (daemon(1)
) для обработки шаблона разветвления.