簡體   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-2024 STACKOOM.COM