簡體   English   中英

如何在 Python 中重定向 stderr?

[英]How to redirect stderr in Python?

我想記錄 Python 腳本的所有輸出。 我試過:

import sys

log = []

class writer(object):
    def write(self, data):
        log.append(data)

sys.stdout = writer()
sys.stderr = writer()

現在,如果我“打印‘東西’”,它就會被記錄下來。 但是,如果我犯了一些語法錯誤,比如“打印‘東西#’,它就不會被記錄下來——它會進入控制台。

如何從 Python 解釋器中捕獲錯誤?

我在這里看到了一個可能的解決方案:

http://www.velocityreviews.com/forums/showpost.php?p=1868822&postcount=3

但是第二個示例登錄到 /dev/null - 這不是我想要的。 我想將它記錄到一個列表中,就像我上面的例子或 StringIO 之類的......

另外,最好我不想創建子進程(並在單獨的線程中讀取它的 stdout 和 stderr)。

我有一個我為工作編寫的軟件,可以將 stderr 捕獲到一個文件中,如下所示:

import sys
sys.stderr = open('C:\\err.txt', 'w')

所以絕對有可能。

我相信您的問題是您正在創建兩個 writer 實例。

也許更像:

import sys

class writer(object):
    log = []

    def write(self, data):
        self.log.append(data)

logger = writer()
sys.stdout = logger
sys.stderr = logger

你不能在 Python 代碼中做任何可以在編譯相同代碼期間捕獲錯誤的事情。 怎么可能? 如果編譯器不能完成代碼的編譯,它就不會運行代碼,所以你的重定向還沒有生效。

這就是您的(不需要的)子進程的用武之地。您可以編寫重定向標准輸出的 Python 代碼,然后調用 Python 解釋器來編譯其他一些代碼。

我想不出一個簡單的方法。 python 進程的標准錯誤存在於比 python 文件對象(C vs. python)低的級別上。

您可以將 python 腳本包裝在第二個 python 腳本中並使用 subprocess.Popen。 您也可以在單個腳本中使用這樣的魔法:

import os
import subprocess
import sys

cat = subprocess.Popen("/bin/cat", stdin=subprocess.PIPE, stdout=subprocess.PIPE)
os.close(sys.stderr.fileno())
os.dup2(cat.stdin.fileno(), sys.stderr.fileno())

然后使用 select.poll() 定期檢查 cat.stdout 以查找輸出。

是的,這似乎有效。

我預見的問題是,大多數時候,python 打印到 stderr 的內容表明它即將退出。 處理此問題的更常用方法是通過異常。

- - - - -編輯

不知何故,我錯過了 os.pipe() 函數。

import os, sys
r, w = os.pipe()
os.close(sys.stderr.fileno())
os.dup2(w, sys.stderr.fileno())

然后從 r 讀取

實際上,如果您使用的是 linux/mac os,則只需使用文件重定向即可。 例如,如果您要運行“a.py”並將它將生成的所有消息記錄到文件“a.out”中,則它只是

python a.py 2>&1 > a.out

第一部分2>&1將 stderr 重定向到 stdout (0: stdin, 1:stdout, 2:stderr),第二部分將其重定向到名為 a.out 的文件。

有關 Linux/Unix 中重定向運算符的更長列表,請參閱https://askubuntu.com/questions/420981/how-do-i-save-terminal-output-to-a-file

要從 Windows 路由輸出和錯誤,您可以在 Python 文件之外使用以下代碼:

python a.py 1> a.out 2>&1

來源: https : //support.microsoft.com/en-us/help/110930/redirecting-error-messages-from-command-prompt-stderr-stdout

從 python 3.5 開始,你可以使用contextlib.redirect_stderr

with open('help.txt', 'w') as f:
    with redirect_stdout(f):
        help(pow)

我發現這種重定向 stderr 的方法特別有用。 本質上,有必要了解您的輸出是 stdout 還是 stderr。 區別? Stdout 是 shell 命令發布的任何輸出(想想“ls”列表),而 sterr 是任何錯誤輸出。

可能是您希望將 shell 命令輸出並僅在它是正常輸出時將其重定向到日志文件。 在這里使用 ls 作為示例,帶有所有文件標志:

# Imports
import sys
import subprocess

# Open file
log = open("output.txt", "w+")

# Declare command
cmd = 'ls -a'
# Run shell command piping to stdout
result = subprocess.run(cmd, stdout=subprocess.PIPE, shell=True)
# Assuming utf-8 encoding
txt = result.stdout.decode('utf-8')
# Write and close file
log.write(txt)
log.close()

如果你想讓它成為一個錯誤日志,你可以用 stderr 做同樣的事情。 它與帶有 stderr 的 stdout 代碼完全相同。 這會將發送到控制台的錯誤消息通過管道傳送到日志。 這樣做實際上也可以防止它淹沒您的終端窗口!

看到這是前一段時間的帖子,但認為這可以節省一些時間:)

import sys
import tkinter

# ********************************************

def mklistenconsswitch(*printf: callable) -> callable:
    def wrapper(*fcs: callable) -> callable:
        def newf(data):
            [prf(data) for prf in fcs]
        return newf
    stdoutw, stderrw = sys.stdout.write, sys.stderr.write
    funcs = [(wrapper(sys.stdout.write, *printf), wrapper(sys.stderr.write, *printf)), (stdoutw, stderrw)]
    def switch():
        sys.stdout.write, sys.stderr.write = dummy = funcs[0]
        funcs[0] = funcs[1]
        funcs[1] = dummy
    return switch

# ********************************************

def datasupplier():
    i = 5.5
    while i > 0:
        yield i
        i -= .5

def testloop():
    print(supplier.__next__())
    svvitch()
    root.after(500, testloop)

root = tkinter.Tk()
cons = tkinter.Text(root)
cons.pack(fill='both', expand=True)
supplier = datasupplier()
svvitch = mklistenconsswitch(lambda text: cons.insert('end', text))
testloop()
root.mainloop()

如果出現錯誤,Python 將不會執行您的代碼。 但是您可以在另一個腳本中導入您的腳本以捕獲異常。 例子:

腳本文件

print 'something#

最終腳本文件

from importlib.machinery import SourceFileLoader

try:
    SourceFileLoader("main", "<SCRIPT PATH>").load_module()
except Exception as e:
    # Handle the exception here

要補充 Ned 的答案,在編譯期間很難即時捕獲錯誤。

您可以在腳本中編寫多個打印語句,並且可以將標准輸出到一個文件,它會在發生錯誤時停止寫入文件。 要調試代碼,您可以檢查最后記錄的輸出並在那之后檢查您的腳本。


像這樣的東西:

# Add to the beginning of the script execution(eg: if __name__ == "__main__":).
from datetime import datetime
dt = datetime.now()
script_dir = os.path.dirname(os.path.abspath(__file__))      # gets the path of the script
stdout_file = script_dir+r'\logs\log'+('').join(str(dt.date()).split("-"))+r'.log'
sys.stdout = open(stdout_file, 'w')

這將創建一個日志文件並將打印語句流式傳輸到該文件。


注意:注意文件路徑中的轉義字符,同時在代碼最后一行的第二行中與 script_dir 連接。 您可能想要類似於原始字符串的東西。 您可以為此檢查此線程

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM