將程序的輸出重定向到檔案和流?


概述

我們將瞭解一些將程序輸出重定向到檔案和標準流(如 STDOut 和 STDERR)的同時的方法。

tee 命令

Tee 是我們最常用的 Linux 命令列工具之一,可用於重定向程序的輸出。它也稱為“teeing”或“piping”。tee 命令接受兩個引數 - 您希望將重定向的輸出儲存到的檔名,以及將用於寫入原始輸入的另一個檔名。

重定向stdout

開始了!我們將看一個簡單的例子,將 ls(列表)命令的輸出重定向到 stdout 和一個名為 out.log 的臨時檔案。

$ ls -C | tee /tmp/out.log
bin   dev  home mnt  proc  run  srv  tmp  var
boot  etc  lib64	 opt  root  sbin  sys  usr

我們可以確認檔案的內容與執行命令生成的輸出匹配。

$ cat /tmp/out.log
bin   dev  home mnt  proc  run srv  tmp  var
boot  etc  lib64	 opt  root  sbin  sys  usr

另一個需要記住的重要一點是,tee 命令的預設行為是替換檔案的內容。但是,如果需要,我們可以使用 -a(追加)命令在檔案的現有內容之後新增新內容。

stdoutstderr 重定向到同一個檔案

在內部,tee 命令充當傳入流的 T 形分流器,以便可以將資料同時定向到輸出流和一個或多個檔案。我們可以利用此知識將程序的 stderrs 重定向到 stdouts 和檔案。

$ (ls -C; cmd_with_err) 2>&1 | tee /tmp/out.log
bin   dev  home mnt  proc  run srv  tmp  var
boot  etc  lib64	 opt  root  sbin  sys  usr
bash: cmd_with_err: command not found

我們可以看到 cmd_with_error 是一個未知命令,這意味著它會產生錯誤訊息。我們將 stderror fd(fd=2)重定向到 stdin fd(fd=1),以便 tee 可以同時從兩個檔案讀取。

或者,我們也可以使用 |& 作為 2>&1| 的簡寫來獲得相同的結果 -

$ (ls -C; cmd_with_err) |& tee /tmp/out.log
bin   dev  home mnt  proc  run srv  tmp  var
boot  etc  lib64	 opt  root  sbin  sys  usr
bash: cmd_with_err: command not found

現在,讓我們驗證 /tmp/out.log 檔案的內容 -

$ cat /tmp/out.log
bin   dev  home mnt  proc  run srv  tmp  var
boot  etc  lib64	 opt  root  sbin  sys  usr
bash: cmd_with_err: command not found

stdoutstderr 重定向到不同的檔案

有時我們可能希望將程序的輸出重定向到兩個不同的檔案。我們可以使用程序替換來呼叫 tee 命令。在詳細介紹 tee() 函式之前,以下是一個啟用 tee() 函式監聽特定 fd(檔案描述符)並寫回原始 fd 流和檔案的模板程式碼片段示例。

fd> >(tee file_name fd>&fd)

我們必須指出,fd 只是一個檔案描述符的示例;實際值對於 stdout 為 1,對於 stderror 為 2,對於 stdin 為 0。

現在,讓我們利用我們對流的理解,將命令的 stdout 流重定向到 /tmp/out,將 stderr 流重定向到 /tmp/err。

$ ((ls -C; cmd_with_err) 1> >(tee /tmp/out.log)) 2> >(tee /tmp/err.log 2>&2)
bin   dev  home mnt  proc  run srv  tmp  var
boot  etc  lib64	 opt  root  sbin  sys  usr
bash: cmd_with_err: command not found

我們可以確認 /tmp/outfile.log 具有正確的輸出,但 /tmp/errorfile.log 具有錯誤的錯誤訊息。

$ cat /tmp/out.log
bin   dev  home mnt  proc  run srv  tmp  var
boot  etc  lib64	 opt  root  sbin  sys  usr
$ cat /tmp/err.log
bash: cmd_with_err: command not found

重定向延遲

呼叫 tee (tee) 命令將程序的輸出定向到檔案和 STDOUT 可能會導致延遲。我們將研究一個我們可能希望避免的特定情況,並學習在發生這種情況時如何減輕它。

場景

開始了!我們將編寫一個 Python 程式,每秒列印一次當前時間。

$ cat time.py
#!/usr/bin/python
from datetime import datetime
import time
import sys
from sys import stdout
while True:
   sys.stdout.write(datetime.today().strftime("%H:%M:%S %p
"
))     time.sleep(1)

如果我們執行此指令碼,我們將看到指令碼打印出的每個時間戳之間有 1 秒的延遲。

$ ./time.py
   9:29:48 PM
   9:29:49 PM
   9:29:50 PM
   9:29:51 PM
   9:29:52 PM
   9:29:53 PM

延遲重定向

現在,讓我們使用“te”命令將此指令碼的輸出重定向到 stdout 和“time.out”日誌。

$ ./time.py | tee time.out

我們將看到,stdout 在很長一段時間內沒有任何輸出列印,然後會一次性打印出一大塊 stdout。

Python 的內部緩衝區大小設定為 1MB,這意味著當寫入 stdout 時,輸出可能會延遲長達 1 秒才能實際出現在螢幕上。緩衝策略會導致寫入 stdout 的內容透過 4096 位元組(或更大)的緩衝區傳遞,從而減少輸出文字到終端所需的 I/O 次數。

對於互動式應用程式,必須避免從一個頁面重定向到另一個頁面的任何延遲。為了解決重定向延遲問題,我們需要尋找減輕它的方法。

緩解措施

解決此問題的一種方法是確保定期將程式的輸出重新整理到控制檯。

$ cat time.py
#!/usr/bin/python
from datetime import datetime
import time
import sys
from sys import stdout
while True:
   sys.stdout.write(datetime.today().strftime("%H:%M:%S %p
"
)) sys.stdout.flush() time.sleep(1) Let's check if the delay has been removed: $ ./time.py | tee time.out 21:46:12 PM 21:46:13 PM

我們可以直接控制應用程式程式碼,因此我們可以更改它。

但是,在許多情況下,程式可能是編譯後的二進位制檔案,我們可能無法訪問其程式碼進行修改。如果我們想避免與寫入 stdout 相關的延遲,我們可以使用 Linux 中的“unbuffer”程式來解決此問題。

讓我們從我們的程式碼中刪除 sys.stdou​t.flus​h() 方法呼叫,並使用 unbuffer ​命令再次執行重定向。

$ unbuffer ./time.py | tee time.out
21:52:22 PM
21:52:23 PM

更改程式碼後,stdout 寫入操作沒有出現意外延遲。

結論

我們查看了將程式的輸出同時重定向到 stdin 和 stdouts 的幾個示例。我們還學習了一些解決緩衝請求導致的重定向延遲問題的技巧。

更新於: 2022-12-26

895 次瀏覽

開啟你的 職業生涯

透過完成課程獲得認證

開始學習
廣告

© . All rights reserved.