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