这是在 Ubuntu 12.04 上,我试图弄清楚如何让 ffmpeg 以递归方式批量将 FLAC 转换为 MP3。如果我cd
进入一个目录并使用
for f in *.flac; do ffmpeg -i "$f" -c:a libmp3lame -q:a 2 "${f/%flac/mp3}"; done
运行得很好。但是,当我尝试这样做时,它不起作用:
for f in "$(find . -type f -name *.flac)"; do ffmpeg -i "$f" -c:a libmp3lame -q:a 2 "${f/%flac/mp3}"; done
它甚至没有抛出任何有用的错误(但无论如何这里是输出,无需抱怨):
evilsoup@enchantment:~/Music/Jean Sibelius$ for f in "$(find . -type f -name *.flac)"; do ffmpeg -i "$f" -c:a libmp3lame -q:a 2 "${f/%flac/mp3}"; done
ffmpeg version git-2012-12-18-b7e085a Copyright (c) 2000-2012 the FFmpeg developers
built on Dec 18 2012 19:23:11 with gcc 4.6 (Ubuntu/Linaro 4.6.3-1ubuntu5)
configuration: --enable-gpl --enable-libfaac --enable-libfdk-aac --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-librtmp --enable-libtheora --enable-libvorbis --enable-libvpx --enable-x11grab --enable-libx264 --enable-nonfree --enable-version3
libavutil 52. 12.100 / 52. 12.100
libavcodec 54. 80.100 / 54. 80.100
libavformat 54. 49.102 / 54. 49.102
libavdevice 54. 3.102 / 54. 3.102
libavfilter 3. 28.100 / 3. 28.100
libswscale 2. 1.103 / 2. 1.103
libswresample 0. 17.102 / 0. 17.102
libpostproc 52. 2.100 / 52. 2.100
./Symphonies 1, 2, 3 & 5 (Oslo Philharmonic Orchestra Conducted by Mariss Jansons) Disc 1/02. Symphony No.1.flac
./Symphonies 1, 2, 3 & 5 (Oslo Philharmonic Orchestra Conducted by Mariss Jansons) Disc 1/03. Symphony No.1.flac
./Symphonies 1, 2, 3 & 5 (Oslo Philharmonic Orchestra Conducted by Mariss Jansons) Disc 1/stripped2.flac
./Symphonies 1, 2, 3 & 5 (Oslo Philharmonic Orchestra Conducted by Mariss Jansons) Disc 1/05. Symphony No.1.flac
./Symphonies 1, 2, 3 & 5 (Oslo Philharmonic Orchestra Conducted by Mariss Jansons) Disc 1/stripped3.flac
./Symphonies 1, 2, 3 & 5 (Oslo Philharmonic Orchestra Conducted by Mariss Jansons) Disc 1/09. Andante festivo.flac
./Symphonies 1, 2, 3 & 5 (Oslo Philharmonic Orchestra Conducted by Mariss Jansons) Disc 1/08. Symphony No.3.flac
./Symphonies 1, 2, 3 & 5 (Oslo Philharmonic Orchestra Conducted by Mariss Jansons) Disc 1/01. Finlandia.flac
./Symphonies 1, 2, 3 & 5 (Oslo Philharmonic Orchestra Conducted by Mariss Jansons) Disc 1/07. Symphony No.3.flac
./Symphonies 1, 2, 3 & 5
我已经单独测试了该命令,并且它可以按预期工作,因此问题与和find
之间的交互有关。find
for
我知道我可以使用find
的-exec
选项来做一些事情,但是我找不到任何方法来进行字符串替换,就像使用 bashfor
循环一样,而且我宁愿不处理一堆file.flac.mp3
s,即使它们可以用一个简单的 来修复rename
。
答案1
命令周围的双引号$(find ...)
使得 find 的输出被视为一个文件名,这显然不是您想要的。
有几种方法可以实现你想要的:
让 find 直接执行 ffmpeg(无管道,无循环):
find . -type f -name *.flac -exec bash -c ' ffmpeg -i "$0" -c:a libmp3lame -q:a 2 "${0/%flac/mp3}" ' {} \;
使用
find ... -print0 | xargs -0 ...
而不是 for,它专门用于以下目的:find . -type f -name *.flac -print0 | xargs -0 -I {} bash -c ' ffmpeg -i "$0" -c:a libmp3lame -q:a 2 "${0/%flac/mp3}" ' {}
改变独立财务顾问仅换行,使用与以前相同的命令,减去双引号:
IFS=$'\n' for f in $(find . -type f -name *.flac); do ffmpeg -i "$f" -c:a libmp3lame -q:a 2 "${f/%flac/mp3}" done unset IFS
该命令
unset IFS
将 IFS 更改回其默认值(在 shell 脚本中不需要,除非它会干扰后续命令)。
答案2
请阅读BashFAQ/020 - Greg 的维基。为了正确地迭代的输出find
,这里有一个方法可以处理文件名中的各种奇怪字符(空格、换行符、通配符等)。
while IFS= read -r -d '' file; do
ffmpeg -i "$f" -c:a libmp3lame -q:a 2 "${f/%flac/mp3}"
done < <(find . -type f -name "*.flac" -print0)
"*.flac"
如果当前目录中有以 结尾的文件,请不要忘记加上引号以防止扩展.flac
。
答案3
除了这里列出的非常好的答案之外,我还发现 GNU Parallel 可以做到这一点:
find . -type f -name "*.flac" | parallel ffmpeg -i {} -c:a libmp3lame -q:a 2 {.}.mp3
{.}
find ... -print0 | parallel -0 ...
从文件名中删除文件扩展名。GNU Parallel 默认使用换行符作为分隔符,因此除非您预计文件名包含换行符,否则无需使用。
顾名思义,GNU Parallel 并行运行进程 - 默认情况下,每个 CPU 核心运行一个作业。因此,它可能会稍微加快速度;在这种情况下,速度不会加快太多,因为 ffmpeg 本身是多线程的……但仍然如此。