在 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
使用 literal*.bar
作为参数运行。该工具尝试删除一个以 literal 命名的文件*.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
来自(前一种情况)的错误将打印到 的标准错误中rm
。来自 shell (后一种情况)的错误将打印到 shell 的标准错误中。
中的重定向仅影响+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
:任何未使用 stderr 重定向调用的命令(如whatever
)都会从 shell 继承 stderr。这意味着在上面的例子中rm
(如果它运行)和whatever
会将其错误消息(如果有)打印到/dev/null
。之后exec 2>/dev/null
您可以调用任意数量的 或 命令,它们都将以/dev/null
为 stderr。如果您确实想抑制所有类型的错误消息,那么这就是方法。
此解决方案适用于多种 shell,而不仅仅是 Zsh。如果您想要重定向交互式 shell 的 stderr,请记住,一些不太复杂的 shell 使用 stderr 来打印其提示。
请注意,进程可能会重定向其自己的 stderr(就像我们的 shell 所做的那样exec 2>…
);或者它可以将错误消息打印到 stdout 或/dev/tty
(它不应该这样做,但技术上可以)。exec 2>/dev/null
因此不是保证您不会看到任何类似错误消息。
之后exec 2>/dev/null
您仍然可以根据需要重定向任何命令的 stderr。例如,如果whatever
我们有:
whatever 2>&7
那么它的错误信息就会转到我们故意保存为文件描述符的原始 stderr 7
。
exec
并不是重定向 shell 的 stderr(或另一个文件描述符)的唯一方法。您可以像rm
在 中一样对待 shell(但不是整个主 shell) rm … 2>/dev/null
。您可以像这样对待子 shell:
( rm *.bar ) 2>/dev/null
或者只是主 shell 解释的一些代码:
{ rm *.bar; } 2>/dev/null
(;
在 Zsh 中这里是可选的,但在某些其他 shell 中是强制性的;我只是希望代码在 Zsh 之外也能工作)。
这些行中的任何一行都可以解决您的问题。在抑制来自 shell 的错误消息的上下文中,这两行相当于++。
请注意,重定向从(
/开始{
并结束于)
/ }
,其范围是有限的。您仍然可以在括号内放置许多命令,因此“有限”的范围实际上可能是整个脚本。或者它可能只是脚本的一部分(包括whatever
本答案前面介绍的内容,无论您想要什么)。重定向在)
/之后停止工作,这一事实}
使这种方法成为使用 保存和恢复 stderr 的巧妙替代方案exec
。
+严格来说:它会影响“ rm
”在成为(或试图成为)之前和之后rm
。请耐心等待。中的重定向rm … 2>/dev/null
开始于由分叉的 shell 即将用rm
可执行文件替换自身。此 shell 在尝试用 替换自身之前会重定向自己的 stderr rm
。执行重定向后报告的任何错误都将转到/dev/null
。例如,如果rm
找不到 ,则已执行的重定向将抑制该command not found
错误。
++从技术上讲( … )
会创建一个子 shell,但{ … }
实际上不会。子 shell 的行为类似于一个单独的 shell 进程,它继承了变量和当前工作目录,但不能更改其父 shell 中的任何内容(例如变量或当前工作目录)。它可能会或可能不会被实现为一个真正独立的进程,重要的是行为。另一方面,{ … }
仅将命令分组用于某些目的(在我们的例子中,目的是重定向)。这并不意味着当您使用时永远不会有子 shell { … }
;例如,在管道中,除了最后一个部分之外的所有部分都是子 shell(在某些 shell 中:包括最后一个在内的所有部分)。这些技术细节在我们的问题上下文中无关紧要,但一般来说,它们会有所不同。