如何抑制 zsh 中的錯誤訊息?

如何抑制 zsh 中的錯誤訊息?

在zsh中

  • rm foo.bar印刷rm: foo.bar: No such file or directory
  • rm foo.bar 2>/dev/null正如我所料,什麼也沒印。

但如果命令包含模式匹配,則錯誤不會被抑制2>/dev/null

  • rm *.bar印刷zsh: no matches found: *.bar
  • rm *.bar 2>/dev/null列印相同。

zsh 中是否有抑制錯誤訊息的通用方法?一簡單的各種錯誤訊息的方法。

答案1

當您嘗試運行rm *.bar並且沒有匹配的文件時*.bar,基本上會發生兩種情況:

  • POSIX 行為是 shellrm以文字*.bar作為參數運作。該工具嘗試刪除一個字面名稱為 的檔案*.bar(就像您運行rm '*.bar'),它失敗並列印類似rm: *.bar: No such file or directory.這就是 POSIX shell ( sh) 和相容 shell 的行為方式。

  • 非 POSIX 行為是 shell 偵測到不存在匹配項,列印類似內容no matches found: *.bar且不執行rm 根本不。這就是 Zsh 預設的行為方式。 (作為比較:在 Bash 中,您可以透過 切換到此行為shopt -s failglob。)

(前一種情況)的錯誤rm被印到 的 stderr 中rm。來自 shell 的錯誤(後一種情況)被印到 shell 的 stderr 中。

中的重定向僅影響+rm *.bar 2>/dev/null的 stderr 。在你的例子中甚至沒有運行。rmrm

要重定向主 shell 的 stderr,您需要exec.我所說的「主 shell」是指您鍵入內容的互動式 shell;或者,如果執行腳本,則 shell 會解釋該腳本。例子:

exec 2>/dev/null

在 Zsh 中你甚至可以關閉它:

exec 2>&-

一個合理的方法是預先複製原始的 stderr,以防萬一您以後需要使用(或恢復)它。範例(請注意,如果您想將此程式碼貼到互動式 Zsh 中,那麼您應該調用setopt interactive_comments第一的):

exec 7>&2           # "save" stderr
exec 2>/dev/null    # redirect
rm *.bar
whatever
exec 2>&7           # restore
exec 7>&-           # close descriptor which is no longer needed

7是一個任意的單位數字,否則不用作檔案描述符。前兩行可以寫成:exec 7>&2 2>/dev/null;同樣,最後兩行也可以。

請注意rm,任何whatever在沒有 stderr 重定向的情況下呼叫的命令(如 )都會從 shell 繼承 stderr。這意味著在上面的示例中rm(如果它曾經運行過)並將whatever其錯誤訊息(如果有)打印到/dev/null.在您可以呼叫任何數字或命令之後exec 2>/dev/null,它們都將/dev/null作為其 stderr。如果您確實想抑制各種錯誤訊息,那麼這就是方法。

該解決方案適用於許多 shell,而不僅僅是 Zsh。如果您想要重新導向互動式 shell 的 stderr,請記住一些不太複雜的 shell 使用 stderr 來列印其提示字元。

請注意,進程可能會重定向其自己的 stderr(就像我們的 shell 所做的那樣exec 2>…);或者它可以將錯誤訊息列印到標準輸出或/dev/tty(它不應該,但技術上可以)。exec 2>/dev/null因此不是保證您不會看到任何看起來像錯誤訊息的訊息。

之後exec 2>/dev/null您仍然可以根據需要重定向任何命令的 stderr。例如,如果whatever我們有:

whatever 2>&7

那麼它的錯誤訊息將轉到我們故意保存為檔案描述符的原始 stderr 7


exec不是重定向 shell 的 stderr(或其他檔案描述子)的唯一方法。您可以像rmrm … 2>/dev/null.您可以像這樣處理子 shell:

( rm *.bar ) 2>/dev/null

或只是主 shell 解釋的一些程式碼:

{ rm *.bar; } 2>/dev/null

;這裡在 Zsh 中是可選的,在其他一些 shell 中是強制的;我只是希望程式碼也能在 Zsh 之外工作)。

這些行中的任何一行都可以解決您的問題。在抑制來自 shell 的錯誤訊息的上下文中,這兩行是等效的++

請注意,重定向從(/開始並以/{結束,其範圍是有限的。您仍然可以將許多命令放在括號內,因此“有限”範圍實際上可能是整個腳本。或者它可能只是腳本的一部分(包括本答案前面介紹的,無論你想要什麼)。事實上,重定向在/之後停止工作,使得這種方法成為使用 保存和恢復 stderr 的巧妙替代方案。)}whatever)}exec


+嚴格來說:它會影響「rm」成為(或試圖成為)之前和之後rm。耐心聽我說。中的重定向rm … 2>/dev/null作為由 a 執行的重定向開始分叉的 shell 即將用rm可執行檔取代自己。該 shell 在嘗試將自身替換為 .stderr 之前會重新導向其自己的 stderr rm。執行重定向後報告的任何錯誤都將轉到/dev/null.例如,如果rm找不到,則已執行的重定向將抑制該command not found錯誤。

++從技術上講( … )創建了一個子shell,但{ … }實際上並沒有。子 shell 的行為就像一個單獨的 shell 進程,它繼承變數和目前工作目錄,但不能更改其父 shell 中的任何內容(例如變數或目前工作目錄)。它可能會也可能不會被實現為一個真正獨立的過程,重要的是行為。 OTOH{ … }僅出於某種目的對命令進行分組(在我們的例子中,目的是重定向)。這並不意味著當您使用{ … };時永遠不會有子 shell。例如,在管道中,除最後一個之外的所有部分無論如何都是子外殼(在某些外殼中:包括最後一個在內的所有部分)。這些技術細節與我們的問題本身無關,但總的來說它們可以產生影響。

相關內容