如何从“find -exec”中传递为 {} 的路径名中删除扩展名?

如何从“find -exec”中传递为 {} 的路径名中删除扩展名?

假设需要从子目录中的视频文件中提取音频,那么如何正确命名生成的文件?

以下单行代码有效,但保留了旧的文件扩展名。

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 需要-nostdinwhile 循环。exec 和 xargs 通常可能比 while 循环更有效率,但由于此实例将由 ffmpeg 进行处理,因此我觉得不会对性能造成太大影响。

相关内容