繁体   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。*
①如果本文未解决您的问题,请点击查看与本文相关的问题
②如果本文未解决您的问题,请向程序员专用AI小助手提问
暂无
暂无

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

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