[英]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.