在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 行為是 shell
rm
以文字*.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 。在你的例子中甚至沒有運行。rm
rm
要重定向主 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(或其他檔案描述子)的唯一方法。您可以像rm
在rm … 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。例如,在管道中,除最後一個之外的所有部分無論如何都是子外殼(在某些外殼中:包括最後一個在內的所有部分)。這些技術細節與我們的問題本身無關,但總的來說它們可以產生影響。