如何grep标准错误流(stderr)?

And*_*sne 121 grep io-redirection ffmpeg

我正在使用 ffmpeg 来获取音频剪辑的元信息。但我无法grep它。

    $ ffmpeg -i 01-Daemon.mp3  |grep -i Duration
    FFmpeg version SVN-r15261, Copyright (c) 2000-2008 Fabrice Bellard, et al.
      configuration: --prefix=/usr --bindir=/usr/bin 
      --datadir=/usr/share/ffmpeg --incdir=/usr/include/ffmpeg --libdir=/usr/lib
      --mandir=/usr/share/man --arch=i386 --extra-cflags=-O2 
      ...
Run Code Online (Sandbox Code Playgroud)

我检查过,这个 ffmpeg 输出被定向到 stderr。

$ ffmpeg -i 01-Daemon.mp3 2> /dev/null
Run Code Online (Sandbox Code Playgroud)

所以我认为 grep 无法读取错误流来捕获匹配的行。我们如何启用 grep 读取错误流?

使用nixCraft链接,我将标准错误流重定向到标准输出流,然后 grep 工作。

$ ffmpeg -i 01-Daemon.mp3 2>&1 | grep -i Duration
  Duration: 01:15:12.33, start: 0.000000, bitrate: 64 kb/s
Run Code Online (Sandbox Code Playgroud)

但是如果我们不想将 stderr 重定向到 stdout 呢?

小智 103

如果您正在使用bash为什么不使用匿名管道,本质上是 phunehehe 所说的简写:

ffmpeg -i 01-Daemon.mp3 2> >(grep -i Duration)

  • 如果你想再次在 stderr 上过滤重定向输出,添加一个 `>&2`,例如 `command 2> >(grep something >&2)` (18认同)
  • 请解释这是如何工作的。`2>` 将 stderr 重定向到文件,我明白了。这从文件 `>(grep -i Duration)` 中读取。但是文件永远不会被存储?这种技术叫什么,以便我可以阅读更多相关信息? (4认同)
  • @MarkoAvlijaš 查找 [过程替换](https://en.wikipedia.org/wiki/Process_substitution)。*“进程替换也可用于捕获通常会转到文件的输出,并将其重定向到进程的输入。”* (4认同)
  • +1 不错!仅使用 Bash,但比其他替代方法更清洁。 (3认同)
  • +1 我用它来检查 `cp` 输出 `cp -r dir* 到 2> >(grep -v "svn")` (2认同)
  • 请注意,进程替换的目标命令是异步运行的。因此,通过 grep 过滤器的 stderr 行可能不会出现在您期望的其余输出位置,即使在您的下一个命令提示符下也是如此。 (2认同)

Gil*_*il' 52

除了从 stdout 到 stdin 之外,没有一个通常的 shell(甚至 zsh)允许管道。但是所有 Bourne 风格的 shell 都支持文件描述符重新分配(如 中所示1>&2)。因此,您可以暂时将 stdout 转移到 fd 3 并将 stderr 转移到 stdout,然后将 fd 3 放回 stdout。如果stuff在 stdout 上产生一些输出并在 stderr 上产生一些输出,并且您想应用filter错误输出而保持标准输出不变,则可以使用{ stuff 2>&1 1>&3 | filter 1>&2; } 3>&1.

$ stuff () {
  echo standard output
  echo more output
  echo standard error 1>&2
  echo more error 1>&2
}
$ filter () {
  grep a
}
$ { stuff 2>&1 1>&3 | filter 1>&2; } 3>&1
standard output
more output
standard error
Run Code Online (Sandbox Code Playgroud)

  • 这种方法有效。不幸的是,就我而言,如果返回非零返回值,它就会丢失 - 对我来说返回的值是 0。这可能并不总是发生,但它发生在我目前正在研究的案例中。有什么办法可以挽救吗? (2认同)
  • @FaheemMitha 不确定你在做什么,但也许`pipestatus` 会有所帮助 (2认同)

Ste*_*n D 25

这类似于 phunehehe 的“临时文件技巧”,但使用命名管道代替,允许您获得更接近输出时的结果,这对于长时间运行的命令非常方便:

$ mkfifo mypipe
$ command 2> mypipe | grep "pattern" mypipe
Run Code Online (Sandbox Code Playgroud)

在这个结构中,stderr 将被定向到名为“mypipe”的管道。由于grep已使用文件参数调用,因此它不会查找 STDIN 以获取其输入。不幸的是,一旦完成,您仍然需要清理该命名管道。

如果您使用的是 Bash 4,则有一个快捷语法command1 2>&1 | command2,即command1 |& command2. 但是,我相信这纯粹是一种语法快捷方式,您仍在将 STDERR 重定向到 STDOUT。

  • `|&` 语法非常适合清理臃肿的 Ruby stderr 堆栈跟踪。我终于可以轻松地grep那些了。 (2认同)

Mik*_*kel 20

Gilles 和 Stefan Lasiewski 的回答都很好,但这种方式更简单:

ffmpeg -i 01-Daemon.mp3 2>&1 >/dev/null | grep "pattern"
Run Code Online (Sandbox Code Playgroud)

我假设您不想ffmpeg's打印标准输出。

这个怎么运作:

  • 先管
    • ffmpeg 和 grep 已启动,ffmpeg 的 stdout 将转到 grep 的 stdin
  • 接下来重定向,从左到右
    • ffmpeg 的 stderr 设置为其标准输出(当前为管道)
    • ffmpeg 的标准输出设置为 /dev/null

  • @Steve 第二个项目符号中没有“with stderr”。您看过http://unix.stackexchange.com/questions/37660/order-of-redirections吗? (2认同)

Ste*_*ski 10

有关这些测试中使用的脚本,请参见下文。

Grep 只能对 stdin 进行操作,因此您必须将 stderr 流转换为 Grep 可以解析的形式。

通常,stdout 和 stderr 都会打印到您的屏幕上:

$ ./stdout-stderr.sh
./stdout-stderr.sh: Printing to stdout
./stdout-stderr.sh: Printing to stderr
Run Code Online (Sandbox Code Playgroud)

要隐藏标准输出,但仍打印标准错误,请执行以下操作:

$ ./stdout-stderr.sh >/dev/null
./stdout-stderr.sh: Printing to stderr
Run Code Online (Sandbox Code Playgroud)

但是 grep 不会在 stderr 上运行!您会期望以下命令抑制包含 'err' 的行,但事实并非如此。

$ ./stdout-stderr.sh >/dev/null |grep --invert-match err
./stdout-stderr.sh: Printing to stderr
Run Code Online (Sandbox Code Playgroud)

这是解决方案。

以下 Bash 语法会将输出隐藏到 stdout,但仍会显示 stderr。首先我们通过管道将 stdout 传输到 /dev/null,然后我们将 stderr 转换为 stdout,因为 Unix 管道只会在 stdout 上运行。您仍然可以对文本进行 grep。

$ ./stdout-stderr.sh 2>&1 >/dev/null | grep err
./stdout-stderr.sh: Printing to stderr
Run Code Online (Sandbox Code Playgroud)

(注意上面的命令then不同./command >/dev/null 2>&1,这是一个很常见的命令)。

这是用于测试的脚本。这将向 stdout 打印一行,向 stderr 打印一行:

#!/bin/sh

# Print a message to stdout
echo "$0: Printing to stdout"
# Print a message to stderr
echo "$0: Printing to stderr" >&2

exit 0
Run Code Online (Sandbox Code Playgroud)


小智 9

bash可以通过常规管道将 stdout 重定向到 stdin 流 -| 它还可以通过以下方式将 stdout 和 stderr 重定向到 stdin|&


Kus*_*nda 6

您可以交换流。这将使您能够访问grep原始标准错误流,同时仍然获得最初进入终端标准输出的输出:

somecommand 3>&2 2>&1 1>&3- | grep 'pattern'
Run Code Online (Sandbox Code Playgroud)

首先创建一个新的文件描述符 (3) 打开以进行输出并将其设置为标准错误流 ( 3>&2)。然后我们将标准错误重定向到标准输出 ( 2>&1)。最后标准输出被重定向到原来的标准错误,并且新的文件描述符被关闭( 1>&3-)。

在你的情况下:

ffmpeg -i 01-Daemon.mp3 3>&2 2>&1 1>&3- | grep -i Duration
Run Code Online (Sandbox Code Playgroud)

测试它:

$ ( echo "error" >&2; echo "output" ) 3>&2 2>&1 1>&3- | grep "error"
output
error

$ ( echo "error" >&2; echo "output" ) 3>&2 2>&1 1>&3- | grep -v "error"
output
Run Code Online (Sandbox Code Playgroud)

  • @Trolzen 这个问题没有指定如何处理原始标准输出流。如果您想丢弃它,请将其重定向到“/dev/null”,而不是将其连接到标准错误流。我的回答是围绕这样一个想法建立的:人们可能仍然希望看到标准输出;因此,我建议交换流。 (2认同)

phu*_*ehe 5

当您将一个命令的输出通过管道传输到另一个命令时(使用|),您只是在重定向标准输出。所以这应该解释为什么

ffmpeg -i 01-Daemon.mp3 | grep -i Duration
Run Code Online (Sandbox Code Playgroud)

不输出你想要的(虽然它确实有效)。

如果您不想将错误输出重定向到标准输出,您可以将错误输出重定向到一个文件,然后再使用 grep

ffmpeg -i 01-Daemon.mp3 2> /tmp/ffmpeg-error
grep -i Duration /tmp/ffmpeg-error
Run Code Online (Sandbox Code Playgroud)