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