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