Как удалить расширение из пути, переданного как {} в `find -exec`?

Учитывая ситуацию, когда аудио нужно извлекать из видеофайлов, находящихся в подкаталогах, как можно было бы правильно именовать получившиеся файлы?

Следующий однострочный работает, но сохраняет старое расширение файла.

find -name "*.mp4" -exec ffmpeg -i {} -vn -acodec copy {}.aac -hide_banner \;

Текущий результат:

foobar.mp4 --> foobar.mp4.aac

Желаемый результат:

foobar.mp4 --> foobar.aac

3 ответа

Решение

Там нет механизма в find Само по себе, что позволяет получить подстроку из того, что заменяется {}, Даже добавив суффикс (как вы сделали: {}.aac) может не поддерживаться. POSIX говорит:

Утилита_имя или аргумент, содержащий только два символа "{}", должны быть заменены текущим путем. Если имя_программы или строка аргумента содержит два символа "{}", а не только два символа "{}", то определяется реализацией, заменяет ли find эти два символа или использует строку без изменений.

Со многими реализациями такие вещи, как foo{}bar Работай, хотя, тем не менее, будь осторожен. Чтобы сделать что-то еще, вам определенно нужен инструмент, который будет манипулировать строкой; обычно оболочка. В твоем случае:

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 …).

С помощью команды (а.findальтернатива), это можно решить с помощью:

      fd -e mp4 -x ffmpeg -i {} -vn -acodec copy {.}.aac -hide_banner

где:

  • fd вызвать команду fd
  • -e mp4 поиск файлов с расширением mp4
  • -x команда exec для каждого результата поиска
  • ffmpeg -i {}заполнитель {} содержит текущий результат поиска, передаваемый в ffmpeg в качестве входных данных.
  • {.} путь вывода без расширения файла (обратите внимание на точку)

Другой способ:

for file in $(find . -name "*mp4")
do
ffmpeg -i $file -vn -acodec copy [other parameters if required] ${file%%.mp4}.aac
done

EDIT: я понятия не имею, почему я мог подумать о таком цикле, когда достаточно более простого:

for file in *.mp4
do
ffmpeg -i $file -vn -acodec copy [other parameters if required] ${file%.mp4}.aac
done

Однако если рекурсивный findдействительно требуется, основываясь на комментарии Скотта, я решил улучшить последний ответ. Я использую цикл while с вводом из поиска через подстановку процесса:

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

Другие вопросы по тегам