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