假设需要从子目录中的视频文件中提取音频,那么如何正确命名生成的文件?
以下单行代码有效,但保留了旧的文件扩展名。
find -name "*.mp4" -exec ffmpeg -i {} -vn -acodec copy {}.aac -hide_banner \;
目前结果:
foobar.mp4 --> foobar.mp4.aac
期望的结果:
foobar.mp4 --> foobar.aac
答案1
本身没有任何机制find
允许您从替换 的任何内容中获取子字符串{}
。甚至添加后缀(如您所做的:{}.aac
)也可能不受支持。POSIX 说:
A实用程序名称或仅包含两个字符“{}”的参数应由当前路径名替换。如果实用程序名称或者参数字符串包含两个字符“{}”,而不仅仅是两个字符“{}”,则由实现定义是否寻找替换这两个字符或使用不做改变的字符串。
虽然许多实现都能够foo{}bar
正常工作,但还是要小心。要执行更多操作,您肯定需要一个可以操纵字符串的工具;通常是一个 shell。对于您的情况:
find . -name "*.mp4" -exec sh -c 'ffmpeg -i "$1" -vn -acodec copy "${1%.*}.aac" -hide_banner' sh {} \;
这里${1%.*}
负责获取不带扩展名的路径名。
您可能想在传递给 的命令字符串中用ffmpeg -i "$1"
替换。请不要这样做。ffmpeg -i "{}"
sh
我的另一个答案解释了为什么后者是错误的(以及为什么find . -name …
比更好find -name …
)。
答案2
使用fd
命令(find
替代方法),可以解决这个问题:
fd -e mp4 -x ffmpeg -i {} -vn -acodec copy {.}.aac -hide_banner
在哪里:
fd
调用 fd 命令-e mp4
搜索带有 mp4 扩展名的文件-x
对每个搜索结果执行命令ffmpeg -i {}
占位符 {} 包含传递给 ffmpeg 作为输入的当前搜索结果{.}
不带文件扩展名的输出路径(注意点)
答案3
另一种方法是:
for file in $(find . -name "*mp4")
do
ffmpeg -i $file -vn -acodec copy [other parameters if required] ${file%%.mp4}.aac
done
编辑: 我不知道为什么我会想到这样的循环,其实更简单的循环就足够了:
for file in *.mp4
do
ffmpeg -i $file -vn -acodec copy [other parameters if required] ${file%.mp4}.aac
done
但是,如果确实需要递归find
,根据 Scott 的评论,我决定改进最后一个答案。我使用 while 循环,输入来自 find 到进程替换:
while IFS= read -r file
do
ffmpeg -nostdin -i $file -vn -acodec copy [other parameters if required] ${file%.mp4}.aac
done< <(find . -name "*.mp4")
请注意,ffmpeg 需要-nostdin
while 循环。exec 和 xargs 通常可能比 while 循环更有效率,但由于此实例将由 ffmpeg 进行处理,因此我觉得不会对性能造成太大影响。