在这个问题的答案是
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
存在于系统中,而不是使用参数arg1
和arg2
命名为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。