如何抑制 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使用 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 。在您的示例中甚至没有运行。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:任何未使用 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 中:包括最后一个在内的所有部分)。这些技术细节在我们的问题上下文中无关紧要,但一般来说,它们会有所不同。

相关内容