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)
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)
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。
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打印标准输出。
这个怎么运作:
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)
您可以交换流。这将使您能够访问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)
当您将一个命令的输出通过管道传输到另一个命令时(使用|),您只是在重定向标准输出。所以这应该解释为什么
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)