干净地并可选地将stderr或stdout重定向到文件

时间:2015-10-22 22:24:31

标签: python-3.x stream control-flow contextmanager exception-safety

我有一个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()

这是有效的,除非中间的代码决定退出。然后我的文件不能保证关闭。无论代码中发生什么,只有部分时间,我怎样才能干净地关闭这些文件? (通常情况下,我会通过shell重定向,但是我在Python中计算文件名并且我不想在各种shell中重新计算它们。而且,我不想把它放在逻辑中是否可以在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

1 个答案:

答案 0 :(得分:0)

有选择地将程序stdoutstderr重定向到文件的最简洁方法是在程序中完全不这样做。相反,通过操作系统的shell进行操作。

在Linux上,如果我想将Python程序的stdout重定向到文件,I'd do this

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

请注意2>重定向stderr输出。

碰巧,Windows上的cmdPowerShell使用相同的语法。

鉴于OP更新的问题描述,上述内容虽然属实,但并不相关。

假设您使用的是Python 3,the built-in print function实际上有一个命名参数" file"这可以让您决定print到哪里。

print(some_object, file=your_own_file_object)

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

否则,您可能会考虑设置sys.stdout and sys.stderr的值,如果您不介意从其他Python程序员那里获得有趣的外观。