繁体   English   中英

如何让'print()','os.system()'和'subprocess.run()'输出显示在控制台和日志文件中?

[英]How to get 'print()', 'os.system()' and 'subprocess.run()' output to be displayed in both console and log file?

提示:本站提供中英文对照查看,鼠标放在中文字句上可显示英文原文。 若本文未解决您的问题,推荐您尝试使用帮您解决。

最初,我有一个简单的程序将整个输出打印到控制台。

初始代码仅在控制台中显示输出

import os, subprocess

print("1. Before")
os.system('ver')                            
subprocess.run('whoami')        
print('\n2. After')

控制台输出

1. Before

Microsoft Windows [Version 10]
user01

2. After

然后,我决定在日志文件(log.txt)上复制一份,同时保持原始输出到控制台。

所以,这是新代码。

import os, subprocess, sys

old_stdout = sys.stdout
log_file = open("log.txt","w")
sys.stdout = log_file

print("1. Before")          # This appear in message.log only, but NOT in console
os.system('ver')            # This appear in console only, but NOT in message.log
subprocess.run('whoami')    # This appear in console only, but NOT in message.log
print('\n2. After')         # This appear in message.log only, but NOT in console

sys.stdout = old_stdout
log_file.close()

不幸的是,这并没有像预期的那样真正奏效。 某些输出仅显示在控制台上( os.system('ver')os.system('ver') subprocess.run('whoami') ),而print()函数仅显示在log.txt文件中,而不再显示在控制台中。

控制台输出

Microsoft Windows [Version 10]
user01

log.txt文件中输出

1. Before

2. After

我希望在console和log.txt文件中获得类似的输出。 这可能吗? 我的新代码出了什么问题? 请让我知道如何解决这个问题。

控制台和log.txt文件中的所需输出

1. Before

Microsoft Windows [Version 10]
user01

2. After

处理此问题的最合适方法是使用日志记录。 这是一个例子:

这是你可以做到的python 2.6+和3.x版本。 (2.6之前不能覆盖print()

log = logging.getLogger()
log.setLevel(logging.INFO)

# How should our message appear?
formatter = logging.Formatter('%(message)s')

# This prints to screen
ch = log.StreamHandler()
ch.setLevel(logging.INFO)
ch.setFormatter(formatter)
log.addHandler(ch)

# This prints to file
fh = log.FileHandler('/path/to/output_file.txt')
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
log.addHandler(fh)

def print(*args, **kwargs):
    log.DEBUG(*args)

该选项允许您使用日志记录级别。 例如,当应用程序开始执行时,您可以在代码中放置调试日志记录。 logLevel交换到logging.DEBUG然后突然,您将该输出转换为屏幕。 请注意,在上面的示例中,我们有2个不同的日志记录级别,一个用于屏幕,另一个用于文件。 是的,这将为每个目的地产生不同的输出。 您可以通过更改两者来使用logging.INFO (或logging.DEBUG等)来解决此问题。 请参阅此处有关日志级别的完整文档。

在上面的例子中,我已经覆盖了print() ,但我建议您只使用log.DEBUG('Variable xyz: {}'.format(xyz))log.INFO('Some stuff that you want printed.)

完整的logging文档。

还有另一种更简单的方法来覆盖,但不是那么强大:

try:
    # Python 2
    import __builtin__
except ImportError:
    # Python 3
    import builtins as __builtin__
logfile = '/path/to/logging_file.log'

def print(*args, **kwargs):
    """Your custom print() function."""
    with open(logfile) as f_out:
        f_out.write(args[0])
        f_out.write('\n')
        # Uncomment the below line if you want to tail the log or something where you need that info written to disk ASAP.
        # f_out.flush()
    return __builtin__.print(*args, **kwargs)

系统没有任何魔术,你的代码需要区别对待stdoutstderr文件指针 例如, stdout是文件指针之一,您可以在下面执行:

log_file_pointer = open('log.txt', 'wt')
print('print_to_fp', file=log_file_pointer)
# Note: the print function will actually call log_file_pointer.write('print_to_fp')

根据您的要求,您希望使魔术函数在单行中处理多个文件指针,您需要一个包装函数如下:

def print_fps(content, files=[]):
    for fi in files:
        print(content, file=fi)
# the argument `file` of print does zero magic, it can only handle one file pointer once. 

然后,你可以让魔法现在发生(在屏幕和文件中输出。)

import sys

log_file_pointer = open('log.txt', 'wt')
print_fps('1. Before', files=[log_file_pointer, sys.stdout])
print_fps('\n2. After', files=[log_file_pointer, sys.stdout])

完成print部分后,让我们继续进行系统调用。 在操作系统中运行任何命令,您将获得默认系统文件指针的返回: stdoutstderr 在python3中,您可以通过subprocess.Popen字节为单位获取这些结果。 在代码下运行时,你想要的应该是stdout的结果。

import subprocess

p = subprocess.Popen("whoami", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = p.communicate()

# stdout: b'user01'
# stdout: b''

再一次,您可以调用上面编写的包装函数,并在stdout和目标file_pointer中生成输出。

print_fps(stdout, files=[log_file_pointer, sys.stdout])

最后,结合上面的所有代码。 (另外还有一个方便的功能。)

import subprocess, sys

def print_fps(content, files=[]):
    for fi in files:
        print(content, file=fi)

def get_stdout(command):
    p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout, stderr = p.communicate()
    # Note: Original idea is to return raw stdout 
    # return stdout
    # Based on the scenario of the @Sabrina, the raw bytes of stdout needs decoding in utf-8 plus replacing newline '\r\n' to be pure
    return stdout.decode().replace('\r\n', '')

log_file_pointer = open('log.txt', 'wt')
print_fps('1. Before', files=[log_file_pointer, sys.stdout])
print_fps(get_stdout('ver'), files=[log_file_pointer, sys.stdout])
print_fps(get_stdout('whoami'), files=[log_file_pointer, sys.stdout])
print_fps('\n2. After', files=[log_file_pointer, sys.stdout])
  • 注意:因为Popen的输出以字节为单位,您可能需要进行解码才能删除b'' 您可以运行stdout.decode()将字节解码为utf-8解码的str。*
问题未解决?试试使用:帮您解决问题。
暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2023 STACKOOM.COM