干净地(可选)将stderr或stdout重定向到文件 [英] Cleanly and optionally redirect stderr or stdout to file

查看:196
本文介绍了干净地(可选)将stderr或stdout重定向到文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Python3脚本,我想有选择地将stdoutstderr重定向到文件.像这样:

# variable declarations
if log_output:
    output_file = open('output.txt', 'w')
    sys.stdout = output_file

if log_errors:
    errors_file = open('errors.txt', 'w')
    sys.stderr = errors_file

# code that uses variables declared above but may exit suddenly

#at the end
if log_output:
    output_file.close()

if log_errors:
    errors_file.close()

这是可行的,除非我中间的代码决定退出.这样就不能保证我的文件是关闭的.无论代码在什么情况下发生更改,并且仅在某些时间出现,我如何干净地关闭这些文件? (通常,我会通过外壳进行重定向,但是我正在使用Python计算文件名,并且我不想在各种外壳中重新计算它们.而且,我也不想提出是否重定向的逻辑在shell脚本中.如果可能,我希望在我的主代码中有这些分支.)

尝试1

似乎上下文管理器将是解决问题的方法,但是,当我尝试使用它们时,我不得不重写我的代码几次,而这不是很漂亮的代码:

if log_output:
    with open('output.txt', 'w') as output_file:
        with contextlib.redirect_stdout(output_file):
            if log_errors:
                with open('errors.txt','w') as errors_file:
                    with contextlib.redirect_stderr(errors_file):
                        # log_output and log_errors
                        # code that uses variables declared above but may exit suddenly
            else:
                # log_output and not log_errors
                # code that uses variables declared above but may exit suddenly
else:
    if log_errors:
        with open('errors.txt', 'w') as errors_file:
            with contextlib.redirect_stderr(errors_file):
                # not log_output and log_errors
                # code that uses variables declared above but may exit suddenly
    else:
        # not log_output and not log_errors
        # code that uses variables declared above but may exit suddenly

尝试2

我决定为此创建一个上下文管理器.我认为它有效,Python并没有对我大吼大叫,但我仍然忍不住觉得它不太像Pythonic,而且我也不完全确定它的安全性.我在奇怪的方向上推动if语句.有更好的方法吗?

@contextlib.contextmanager
def opt_stream(stream, name = None):
    if name:
        file = open(name,'w')
        yield file
        file.close()
    else:
        yield stream

output_name, errors_name = None, None

if log_output:
    output_name = 'outputs.txt'
if log_errors:
    errors_name = 'errors.txt'

with opt_stream(sys.stdout, output_name) as output_file:
    with opt_stream(sys.stderr, errors_name) as errors_file:
        with contextlib.redirect_stdout(output_file):
            with contextlib.redirect_stderr(errors_file):
                # code that uses variables declared above but may exit suddenly

解决方案

(可选)将程序的stdoutstderr重定向到文件的最简洁的方法是,根本不要在程序中这样做.相反,请通过操作系统的外壳进行操作.

在Linux上,如果我想将Python程序的stdout重定向到文件,我' d为此:

$ python something.py > stdout.log
$ python something_else.py 2> stderr.log

请注意2>用于重定向stderr输出.

发生这种情况时, cmd 内置的print函数实际上有一个命名参数"file",可让您决定将print放置在何处.

print(some_object, file=your_own_file_object)

file可以是任何类似文件的对象(stdoutstderr是).您可以只传递 open() 的结果,否则就会发疯并使用 io模块.无论哪种情况,您都只需要维护一个变量(其值可能是sys.stdout的值),并始终将其传递给print调用,然后只要您决定将输出内容输出到哪里,就只需设置该变量即可.

否则,您可以考虑设置 sys.stdoutsys.stderr ,如果您不介意从其他Python程序员那里获得有趣的表情的话.

I have a Python3 script and I want to optionally redirect stdout and stderr to a file. Something like this:

# variable declarations
if log_output:
    output_file = open('output.txt', 'w')
    sys.stdout = output_file

if log_errors:
    errors_file = open('errors.txt', 'w')
    sys.stderr = errors_file

# code that uses variables declared above but may exit suddenly

#at the end
if log_output:
    output_file.close()

if log_errors:
    errors_file.close()

This works, unless my code in the middle decides to quit. Then my files aren't guaranteed to be closed. How can I cleanly close these files no matter what happens in the code and only some of the time? (Normally, I would redirect through the shell, but I'm computing the file names in Python and I don't want to recompute them in various shells. Also, I don't want to put the logic for whether or not to redirect in a shell script. I want those branches in my main code if possible.)

Attempt 1

It seems like context managers would be the way to here, but, when I try to use them, I have to rewrite my code several times and it's not pretty code:

if log_output:
    with open('output.txt', 'w') as output_file:
        with contextlib.redirect_stdout(output_file):
            if log_errors:
                with open('errors.txt','w') as errors_file:
                    with contextlib.redirect_stderr(errors_file):
                        # log_output and log_errors
                        # code that uses variables declared above but may exit suddenly
            else:
                # log_output and not log_errors
                # code that uses variables declared above but may exit suddenly
else:
    if log_errors:
        with open('errors.txt', 'w') as errors_file:
            with contextlib.redirect_stderr(errors_file):
                # not log_output and log_errors
                # code that uses variables declared above but may exit suddenly
    else:
        # not log_output and not log_errors
        # code that uses variables declared above but may exit suddenly

Attempt 2

I decided to make a context manager for it. I think it works, and Python's not yelling at me, but I still can't help but feel it's not too Pythonic and I'm not completely sure it's safe. I'm pushing the if statements in odd directions. Is there a better way?

@contextlib.contextmanager
def opt_stream(stream, name = None):
    if name:
        file = open(name,'w')
        yield file
        file.close()
    else:
        yield stream

output_name, errors_name = None, None

if log_output:
    output_name = 'outputs.txt'
if log_errors:
    errors_name = 'errors.txt'

with opt_stream(sys.stdout, output_name) as output_file:
    with opt_stream(sys.stderr, errors_name) as errors_file:
        with contextlib.redirect_stdout(output_file):
            with contextlib.redirect_stderr(errors_file):
                # code that uses variables declared above but may exit suddenly

解决方案

The cleanest way to optionally redirect your program's stdout or stderr to a file is to not do so in your program at all. Instead, do it through your operating system's shell.

On Linux, if I wanted to redirect a Python program's stdout to a file, I'd do this:

$ python something.py > stdout.log
$ python something_else.py 2> stderr.log

Note the 2> for redirecting stderr output.

As it happens, cmd and PowerShell on Windows use the same syntax.


The above, while true, isn't relevant given the OP's updated problem description.

Assuming you're using Python 3, the built-in print function actually has a named parameter "file" that lets you decide where to print to.

print(some_object, file=your_own_file_object)

file can be any file-like object (which stdout and stderr are). You could just pass in the result of open(), or get crazy and screw around with the io module. Whatever the case, you just have to maintain a variable (whose value is possibly that of sys.stdout) and always pass that in to print calls, and then just set that variable whenever you decide where to output something to.

Otherwise, you might considering setting the value of sys.stdout and sys.stderr, if you don't mind getting funny looks from other Python programmers.

这篇关于干净地(可选)将stderr或stdout重定向到文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆