it-swarm.cn

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

我正在使用ffmpeg获取音频剪辑的元信息。但是我无法重复。

    $ 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读取错误流?

使用 nixCraft 链接,我将标准错误流重定向到标准输出流,然后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怎么办?

90
Andrew-Dufresne

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

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

66
Jé Queue

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

这类似于phunehehe的“临时文件技巧”,但改用命名管道,使您可以在输出结果时稍微接近结果,这对于长时间运行的命令可能非常方便:

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

在此构造中,stderr将定向到名为“ mypipe”的管道。由于grep已通过文件参数调用,因此它不会寻找STDIN作为输入。不幸的是,完成后,您仍然必须清理该命名管道。

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

22
Steven D

Gilles和Stefan Lasiewski的答案都不错,但是这种方式比较简单:

ffmpeg -i 01-Daemon.mp3 2>&1 >/dev/null | grep "pattern"

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

怎么运行的:

  • 首先管道
    • ffmpeg和grep启动,ffmpeg的stdout转到grep的stdin
  • 重定向从左到右
    • ffmpeg的stderr设置为它的stdout是什么(当前是管道)
    • ffmpeg的标准输出设置为/ dev/null
11
Mikel

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

Grep只能在stdin上运行,因此,您必须以Grep可以解析的形式转换stderr流。

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

$ ./stdout-stderr.sh
./stdout-stderr.sh: Printing to stdout
./stdout-stderr.sh: Printing to 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

(请注意,上面的命令是不同,然后是./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
8
Stefan Lasiewski

您可以交换流。这将使您能够grep原始标准错误流,同时仍然可以在终端中获取原始输出到标准输出的输出:

somecommand 3>&2 2>&1 1>&3- | grep 'pattern'

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

在您的情况下:

ffmpeg -i 01-Daemon.mp3 3>&2 2>&1 1>&3- | grep -i Duration

测试它:

$ ( 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
3
Kusalananda

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

ffmpeg -i 01-Daemon.mp3 | grep -i Duration

不会输出您想要的内容(尽管它确实可以工作)。

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

ffmpeg -i 01-Daemon.mp3 2> /tmp/ffmpeg-error
grep -i Duration /tmp/ffmpeg-error
3
phunehehe

bash可以通过常规管道将stdout重定向到stdin流-|它也可以通过|&将stdout和stderr都重定向到stdin

1
0x00

在这种情况下,我喜欢使用rc Shell。

首先安装该软件包(小于1MB)。

这是一个示例,说明如何丢弃stdout并将stderr传递到rc中的grep:

find /proc/ >[1] /dev/null |[2] grep task

您无需离开Bash就可以做到:

rc -c 'find /proc/ >[1] /dev/null |[2] grep task'

您可能已经注意到,语法很简单,这使它成为我的首选解决方案。

您可以在管道字符之后指定要在括号中管道传输的文件描述符。

标准文件描述符的编号如下:

  • 0:标准输入
  • 1:标准输出
  • 2:标准误差
1
Rolf

Bash子流程示例的一个变体:

将两行回显到stderr和tee stderr到文件,grep tee并通过管道返回到stdout

(>&2 echo -e 'asdf\nfff\n') 2> >(tee some.load.errors | grep 'fff' >&1)

标准输出:

fff

some.load.errors(例如stderr):

asdf
fff
0
jmunsch