
[英]The reason 'subprocess.run' is better than 'os.system' for beginner
[英]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.)
还有另一种更简单的方法来覆盖,但不是那么强大:
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)
系统没有任何魔术,你的代码需要区别对待stdout和stderr等文件指针 。 例如, 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
部分后,让我们继续进行系统调用。 在操作系统中运行任何命令,您将获得默认系统文件指针的返回: stdout和stderr 。 在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])
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.