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

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

我正在使用 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 
      ...

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

$ ffmpeg -i 01-Daemon.mp3 2> /dev/null

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

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

$ ffmpeg -i 01-Daemon.mp3 2>&1 | grep -i Duration
  Duration: 01:15:12.33, start: 0.000000, bitrate: 64 kb/s

但是如果我们不想将 stderr 重定向到 stdout 该怎么办?

答案1

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

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

答案2

|除了从 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

Ksh、bash 和 zsh 支持任意文件描述符上的管道,但它们的工作方式不同:一个命令是主命令,您可以将其输入或输出通过管道传输到另一个命令。工艺替代在后台运行命令,其标准输入或标准输出连接到管道,您可以在主命令的重定向中使用该管道。这里您想要过滤标准错误,因此将其重定向到输出进程替换。

$ stuff () {
  echo standard output
  echo more output
  echo standard error 1>&2
  echo more error 1>&2
}
$ filter () {
  grep a
}
$ stuff 2> >(filter)
standard output
more output
standard error

请注意,有两个>字符之间有一个空格:这是2>为了重定向标准输出并>(…)进行进程替换。该空格是必要的,因为2>>…会被解析为追加重定向。

答案3

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

$ mkfifo mypipe
$ command 2> mypipe | grep "pattern" mypipe

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

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

答案4

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

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

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

$ ./stdout-stderr.sh
./stdout-stderr.sh: Printing to stdout
./stdout-stderr.sh: Printing to stderr

要隐藏 stdout,但仍打印 stderr,请执行以下操作:

$ ./stdout-stderr.sh >/dev/null
./stdout-stderr.sh: Printing to stderr

但 grep 不会在 stderr 上运行!您可能希望以下命令抑制包含“err”的行,但事实并非如此。

$ ./stdout-stderr.sh >/dev/null |grep --invert-match err
./stdout-stderr.sh: Printing to stderr

这是解决方案。

以下 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

(注意上面的命令是不同的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

相关内容