Почему это не работает для цикла?
Это на 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
"s -exec
вариант, но я не могу найти способ сделать подстановку строк, как я могу с помощью bash for
цикл, и я бы предпочел не иметь кучу file.flac.mp3
чтобы иметь дело с, даже если они могут быть исправлены с помощью простого rename
,
3 ответа
Двойные кавычки вокруг $(find ...)
Команда make для вывода результатов поиска в виде одного имени файла, что, очевидно, не то, что вам нужно.
Есть несколько способов достичь того, что вы хотите:
Заставьте 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 только на новую строку, используйте ту же команду, что и раньше, за исключением двойных кавычек:
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 обратно к его значению по умолчанию (не требуется внутри сценария оболочки, если это не мешает последующим командам).
Пожалуйста, прочитайте BashFAQ/020 - Wiki Грега. Чтобы правильно перебрать вывод 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
,
Помимо перечисленных здесь очень хороших ответов, я обнаружил, что GNU Parallel может делать это:
find . -type f -name "*.flac" | parallel ffmpeg -i {} -c:a libmp3lame -q:a 2 {.}.mp3
{.}
удаляет расширение файла из имени файла. GNU Parallel по умолчанию использует символ новой строки в качестве разделителя, поэтому нет необходимости использовать find ... -print0 | parallel -0 ...
если вы не ожидаете имена файлов, содержащие переводы строк.
Как следует из названия, GNU Parallel запускает процессы параллельно - по умолчанию одно задание на ядро процессора. Так что это может немного ускорить процесс; не так много в этом конкретном случае, так как ffmpeg сам по себе является многопоточным ... но все же.