3

Это на 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 ответа3

3

Двойные кавычки вокруг команды $(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 только на новую строку, используйте ту же команду, что и раньше, за исключением двойных кавычек:

    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 обратно на его значение по умолчанию (не требуется внутри сценария оболочки, если это не мешает последующим командам).

3

Пожалуйста, прочитайте 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 .

0

Помимо перечисленных здесь очень хороших ответов, я обнаружил, что 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 сам по себе является многопоточным ... но все же.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .