Перенаправить stdout в файл на Python?
как перенаправить stdout в произвольный файл в Python?
когда долговременный скрипт Python (например, веб-приложение) запускается из сеанса ssh и backgounded, а сеанс ssh закрыт, приложение вызовет IOError и завершится неудачей в тот момент, когда он попытается записать в stdout. Мне нужно было найти способ сделать вывод приложения и модулей в файл, а не stdout, чтобы предотвратить сбой из-за IOError. В настоящее время я использую nohup для перенаправления вывода на a файл, и это делает работу, но мне было интересно, есть ли способ сделать это без использования nohup, из любопытства.
Я уже пробовал sys.stdout = open('somefile', 'w')
, но это, похоже, не мешает некоторым внешним модулям все еще выводить на терминал (или, возможно,sys.stdout = ...
линия вообще не стреляла). Я знаю, что он должен работать с более простыми сценариями, которые я тестировал, но у меня также не было времени для тестирования в веб-приложении.
10 ответов:
Если вы хотите сделать перенаправление в скрипте Python, установите
sys.stdout
для объекта file делает трюк:import sys sys.stdout = open('file', 'w') print 'test'
гораздо более распространенным методом является использование перенаправления оболочки при выполнении (то же самое в Windows и Linux):
$ python foo.py > file
здесь
contextlib.redirect_stdout()
функции в Python 3.4:from contextlib import redirect_stdout with open('help.txt', 'w') as f: with redirect_stdout(f): print('it now prints to `help.text`')
это похоже на:
import sys from contextlib import contextmanager @contextmanager def redirect_stdout(new_target): old_target, sys.stdout = sys.stdout, new_target # replace sys.stdout try: yield new_target # run some code with the replaced stdout finally: sys.stdout = old_target # restore to the previous value
это может быть использовано на более ранних версиях Python. Последняя версия не является многоразовые. Его можно сделать одним при желании.
он не перенаправляет stdout на уровне дескрипторов файлов, например:
import os from contextlib import redirect_stdout stdout_fd = sys.stdout.fileno() with open('output.txt', 'w') as f, redirect_stdout(f): print('redirected to a file') os.write(stdout_fd, b'not redirected') os.system('echo this also is not redirected')
b'not redirected'
и'echo this also is not redirected'
не перенаправляются .для перенаправления на уровень дескриптора файла,
os.dup2()
можно использовать:import os import sys from contextlib import contextmanager def fileno(file_or_fd): fd = getattr(file_or_fd, 'fileno', lambda: file_or_fd)() if not isinstance(fd, int): raise ValueError("Expected a file (`.fileno()`) or a file descriptor") return fd @contextmanager def stdout_redirected(to=os.devnull, stdout=None): if stdout is None: stdout = sys.stdout stdout_fd = fileno(stdout) # copy stdout_fd before it is overwritten #NOTE: `copied` is inheritable on Windows when duplicating a standard stream with os.fdopen(os.dup(stdout_fd), 'wb') as copied: stdout.flush() # flush library buffers that dup2 knows nothing about try: os.dup2(fileno(to), stdout_fd) # $ exec >&to except ValueError: # filename with open(to, 'wb') as to_file: os.dup2(to_file.fileno(), stdout_fd) # $ exec > to try: yield stdout # allow code to be run with the redirected stdout finally: # restore stdout to its previous value #NOTE: dup2 makes stdout_fd inheritable unconditionally stdout.flush() os.dup2(copied.fileno(), stdout_fd) # $ exec >&copied
тот же пример работает теперь, если
stdout_redirected()
вместоredirect_stdout()
:import os import sys stdout_fd = sys.stdout.fileno() with open('output.txt', 'w') as f, stdout_redirected(f): print('redirected to a file') os.write(stdout_fd, b'it is redirected now\n') os.system('echo this is also redirected') print('this is goes back to stdout')
вывод, который ранее был напечатан на stdout теперь идет в
output.txt
покаstdout_redirected()
контекстный менеджер активен.Примечание:
stdout.flush()
не заподлицо C stdio буферы на Python 3, где I / O реализуется непосредственно наread()
/
вы можете попробовать это слишком много лучше
import sys class Logger(object): def __init__(self, filename="Default.log"): self.terminal = sys.stdout self.log = open(filename, "a") def write(self, message): self.terminal.write(message) self.log.write(message) sys.stdout = Logger("yourlogfilename.txt") print "Hello world !" # this is should be saved in yourlogfilename.txt
другие ответы не охватывали случай, когда вы хотите, чтобы разветвленные процессы делились вашим новым stdout.
для этого:
from os import open, close, dup, O_WRONLY old = dup(1) close(1) open("file", O_WRONLY) # should open on 1 ..... do stuff and then restore close(1) dup(old) # should dup to 1 close(old) # get rid of left overs
цитата из PEP 343 -- оператор" с" (добавил инструкцию import):
перенаправить stdout временно:
import sys from contextlib import contextmanager @contextmanager def stdout_redirected(new_stdout): save_stdout = sys.stdout sys.stdout = new_stdout try: yield None finally: sys.stdout = save_stdout
используется следующим образом:
with open(filename, "w") as f: with stdout_redirected(f): print "Hello world"
это не потокобезопасно, конечно, но и не делает этот же танец вручную. В однопоточных программах (например, в скриптах) это популярный способ делать вещи.
на основе этого ответа:https://stackoverflow.com/a/5916874/1060344, Вот еще один способ, который я понял, который я использую в одном из моих проектов. За то, что вы замените
sys.stderr
илиsys.stdout
С, вы должны убедиться, что замена соответствуетfile
интерфейс, особенно если это то, что вы делаете, потому что stderr/stdout используются в какой-то другой библиотеке, которая не находится под вашим контролем. Эта библиотека может использовать другие методы файла объект.проверьте этот способ, где я все еще позволяю все делать stderr / stdout (или любой файл, если на то пошло) , а также отправить сообщение в файл журнала с помощью средства ведения журнала Python (но вы действительно можете сделать что-нибудь с этим):
class FileToLogInterface(file): ''' Interface to make sure that everytime anything is written to stderr, it is also forwarded to a file. ''' def __init__(self, *args, **kwargs): if 'cfg' not in kwargs: raise TypeError('argument cfg is required.') else: if not isinstance(kwargs['cfg'], config.Config): raise TypeError( 'argument cfg should be a valid ' 'PostSegmentation configuration object i.e. ' 'postsegmentation.config.Config') self._cfg = kwargs['cfg'] kwargs.pop('cfg') self._logger = logging.getlogger('access_log') super(FileToLogInterface, self).__init__(*args, **kwargs) def write(self, msg): super(FileToLogInterface, self).write(msg) self._logger.info(msg)
вам нужен терминальный мультиплексор, как ни tmux или экран GNU
Я удивлен, что небольшой комментарий Райана Амоса к исходному вопросу является единственным упоминанием о решении, гораздо более предпочтительном для всех остальных, независимо от того, насколько умным может быть обман python и сколько голосов они получили. В дополнение к комментарию Райана, tmux является хорошей альтернативой GNU screen.
но принцип тот же: если вы когда-нибудь найти вы хотите оставить работу терминала во время выхода из системы, отправиться в кафе за бутербродом, поп в ванную комнату, пойти домой (и т. д.), а затем позже снова подключиться к вашей терминальной сессии из любого места или любого компьютера, Как будто вы никогда не были далеко, терминальные мультиплексоры the ответ. Думайте о них как о VNC или удаленном рабочем столе для терминальных сеансов. Все остальное-это обходной путь. В качестве бонуса, когда босс и / или партнер входит, и вы случайно ctrl-w / cmd-w ваш терминал окно вместо окна Вашего браузера с его изворотливым контентом, вы не потеряете последние 18 часов-стоит обработки!
программы, написанные на других языках (например, C), должны делать специальную магию (называемую двойным разветвлением) специально для отсоединения от терминала (и предотвращения процессов зомби). Поэтому, я думаю, что лучшее решение-это подражать им.
плюс повторного выполнения вашей программы, вы можете выбрать перенаправления в командной строке, например
/usr/bin/python mycoolscript.py 2>&1 1>/dev/null
см. Этот пост для получения дополнительной информации: в чем причина выполнения двойной вилки при создании демона?
вот такой вариант Юда Prawira ответ:
- реализовать
flush()
и все атрибуты файла- напишите его как contextmanager
- захват
stderr
и.
import contextlib, sys @contextlib.contextmanager def log_print(file): # capture all outputs to a log file while still printing it class Logger: def __init__(self, file): self.terminal = sys.stdout self.log = file def write(self, message): self.terminal.write(message) self.log.write(message) def __getattr__(self, attr): return getattr(self.terminal, attr) logger = Logger(file) _stdout = sys.stdout _stderr = sys.stderr sys.stdout = logger sys.stderr = logger try: yield logger.log finally: sys.stdout = _stdout sys.stderr = _stderr with log_print(open('mylogfile.log', 'w')): print('hello world') print('hello world on stderr', file=sys.stderr) # you can capture the output to a string with: # with log_print(io.StringIO()) as log: # .... # print('[captured output]', log.getvalue())