什么是Bash中的stream量控制?

在这个问题的答案是

find . -type f -name \*.mp4 -exec sh -c 'ffprobe "$0" 2>&1 | grep -q 1920x1080 && echo "$0"' {} \; 

这将输出所有的1920x1080 MP4文件。

我不明白为什么sh -c在那里。 如果我删除它,那么它不会find任何东西。

作者说

需要新的shell来处理exec'd命令中的stream量控制。

但我想我错过了一些基本的知识来理解答案。

任何人都可以解释为什么ffprobe必须在那里,为什么它只能在ffprobe打开一个新的shell?

-exec选项需要一系列参数:

 find . -exec arg0 arg1 arg2 ... \; 

如果你把参数放在引号中

 find . -exec "arg0 arg1 arg2" \; 

那么“arg0 arg1 arg2”被视为单个参数。 它会期望带有空格的命令arg0 arg1 arg2存在于系统中,而不是使用参数arg1arg2命名为arg0的命令。

如果你使用没有sh -c查找,那么你会有这样的:

 find . -type f -name \*.mp4 -exec 'ffprobe "{}" 2>&1 | grep -q 1920x1080 && echo "{}"' \; 

这将意味着find会查找一个名为ffprobe "$0" ....的命令,不传递任何参数 – 没有这样的命令。 有一个叫做ffprobe的命令,它ffprobe参数,这就是你所需要的。 一种可能性是做这样的事情:

 find . -type f -name \*.mp4 -exec ffprobe '$0' 2>&1 | grep -q 1920x1080 && echo '{}' \; 

但是,这不起作用,因为输出重定向2>&1和管道| 和命令序列运算符&&都将被视为不同于你想要的。

为了解决这个问题,他们使用另外一个shell。 这与创建脚本来完成这项工作类似:

 find . -type f -name \*.mp4 -exec myscript {} \; 

但是,而不是一个单独的脚本,一切都在一条线上。

find除了用文件名替换{}以外,不执行任何处理,直接exec作为命令给exec的参数(即它将第一个参数作为具有以下参数的应用程序调用)作为该命令的参数。 也就是说,它没有实现像管道或输入重定向这样的shell特性。 在你的情况下,该命令包含管道和输入重定向。 所以你需要通过sh运行命令,以便sh处理这些命令。

区别在于find的实现。 Find使用Linux / Unix(Posix)的fork()exec()调用的特性来产生一个新的进程,并将带有参数的命令传递给它。 exec()调用运行一个可执行文件(binary或shebang解释程序),将参数作为参数字符串直接传递给它。 没有处理这些参数。

因此shell的约定是不能解释的。 例如$0 2>&1和'|' 等等作为参数传递给fprobe 。 不是你想要的。 通过添加sh -c ,你告诉find来执行一个shell ,将其余的行传给它来解释。

请注意,通过将命令放入shebang脚本并从find调用此脚本,可以获得相同的效果。 这里exec'ed脚本会加载shell。