在 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 中的错误消息?各种错误消息的一种简单方法。
在 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 中的错误消息?各种错误消息的一种简单方法。
当您尝试运行
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
不运行。这就是 Zsh 默认的行为方式。(作为比较:在 Bash 中,您可以通过 切换到此行为。)rm
shopt -s failglob
来自
rm
(前一种情况)的错误被打印到rm
. 来自 shell 的错误(后一种情况)被打印到 shell 的 stderr。中的重定向仅影响+
rm *.bar 2>/dev/null
的 stderr 。在你的例子中甚至没有运行。rm
rm
要重定向您需要的主 shell 的标准错误
exec
。“主外壳”是指您键入的交互式外壳;或者,在运行脚本的情况下,解释脚本的 shell。例子:在 Zsh 你甚至可以关闭它:
一个合理的方法是事先复制原始的 stderr,以防以后需要使用(或恢复)它。示例(请注意,如果您想将此代码粘贴到交互式 Zsh 中,则应
setopt interactive_comments
首先调用):这
7
是一个任意的单位数字,否则未用作文件描述符。前两行可以写成一个exec 7>&2 2>/dev/null
:同样最后两行可以。注意
rm
或任何whatever
没有 stderr 重定向的命令(如 )都会从 shell 继承 stderr。这意味着在上面的示例中rm
(如果它曾经运行过)并将whatever
其错误消息(如果有)打印到/dev/null
. 在exec 2>/dev/null
您可以调用任何数字或命令之后,它们都将具有/dev/null
它们的标准错误。如果您真的想抑制各种错误消息,那么这就是方法。该解决方案适用于许多 shell,不仅在 Zsh 中。如果您想重定向交互式 shell 的 stderr,请记住一些不太复杂的 shell 使用 stderr 来打印它们的提示。
注意一个进程可能会重定向它自己的 stderr(就像我们的 shell 用 做的那样
exec 2>…
);或者它可以将错误消息打印到标准输出或打印到/dev/tty
(它不应该,但技术上可以)。因此exec 2>/dev/null
不能保证您不会看到任何看起来像错误消息的消息。之后
exec 2>/dev/null
您仍然可以按需重定向任何命令的标准错误。例如,如果whatever
我们有:然后它的错误消息将转到我们故意保存为文件描述符的原始 stderr
7
。exec
不是重定向 shell 的 stderr(或另一个文件描述符)的唯一方法。您可以像对待rm
.rm … 2>/dev/null
您可以像这样对待子shell:或者只是一些由主外壳解释的代码:
(
;
这里在 Zsh 中是可选的,在其他一些 shell 中是必需的;我只是希望代码也能在 Zsh 之外工作)。这些行中的任何一条都可以解决您的问题。在抑制来自 shell 的错误消息的上下文中,这两行是等效的++。
注意重定向开始于
(
/{
结束于)
/}
,它的范围是有限的。您仍然可以在括号内放置许多命令,因此“有限”范围实际上可能是整个脚本。或者它可能只是脚本的一部分(包括whatever
这个答案前面介绍的,无论你想要什么)。)
重定向在/之后停止工作的事实}
使这种方法成为使用exec
.+严格来说:它
rm
在它成为(或试图成为)之前和之后影响“”rm
。忍受我。中的重定向rm … 2>/dev/null
开始于由即将用rm
executable替换自身的分叉 shell 执行的重定向。这个 shell 在尝试将自己替换为rm
. 它在执行重定向后报告的任何错误都将转到/dev/null
. 例如,如果rm
找不到,那么已经执行的重定向将抑制command not found
错误。++从技术上讲
( … )
,创建一个子shell,{ … }
没有。子shell 的行为类似于一个单独的shell 进程,它继承变量和当前工作目录,但不能更改其父shell 中的任何内容(如变量或当前工作目录)。它可能会也可能不会被实现为一个真正独立的过程,重要的是行为。OTOH{ … }
仅出于某种目的对命令进行分组(在我们的例子中,目的是重定向)。这并不意味着您使用{ … }
;时永远不会有子外壳。例如,在管道中,除了最后一个之外的所有部分都是子外壳(在某些外壳中:所有部分包括最后一个)。这些技术细节与我们的问题无关,但总的来说它们可以产生影响。