Как рекурсивно изменить расширение нескольких файлов из командной строки?
У меня много файлов с .abc
расширение и хотите изменить их на .edefg
Как это сделать из командной строки?
У меня есть корневая папка со многими подпапками, поэтому решение должно работать рекурсивно.
9 ответов
Переносимый способ (который будет работать на любой POSIX-совместимой системе):
find /the/path -depth -name "*.abc" -exec sh -c 'mv "$1" "${1%.abc}.edefg"' _ {} \;
В bash4 вы можете использовать globstar для получения рекурсивных глобусов (**):
shopt -s globstar
for file in /the/path/**/*.abc; do
mv "$file" "${file%.abc}.edefg"
done
(Perl) rename
Команда в Ubuntu может переименовывать файлы, используя синтаксис регулярных выражений perl, который вы можете комбинировать с globstar или find
:
# Using globstar
shopt -s globstar
files=(/the/path/**/*.abc)
# Best to process the files in chunks to avoid exceeding the maximum argument
# length. 100 at a time is probably good enough.
# See http://mywiki.wooledge.org/BashFAQ/095
for ((i = 0; i < ${#files[@]}; i += 100)); do
rename 's/\.abc$/.edefg/' "${files[@]:i:100}"
done
# Using find:
find /the/path -depth -name "*.abc" -exec rename 's/\.abc$/.edefg/' {} +
Также см. http://mywiki.wooledge.org/BashFAQ/030
Это сделает необходимую задачу, если все файлы находятся в одной папке
rename 's/.abc$/.edefg/' *.abc
Чтобы переименовать файлы рекурсивно, используйте это:
find /path/to/root/folder -type f -name '*.abc' -print0 | xargs -0 rename 's/.abc$/.edefg/'
Одна проблема с рекурсивными переименованиями заключается в том, что какой бы метод вы ни использовали для поиска файлов, он передает весь путь к rename
, а не только имя файла. Это затрудняет выполнение сложных переименований во вложенных папках.
я использую find
"s -execdir
действие, чтобы решить эту проблему. Если вы используете -execdir
вместо -exec
указанная команда запускается из подкаталога, содержащего соответствующий файл. Таким образом, вместо того, чтобы пройти весь путь к rename
, это только проходит ./filename
, Это значительно облегчает написание регулярного выражения.
find /the/path -type f \
-name '*.abc' \
-execdir rename 's/\.\/(.+)\.abc$/version1_$1.abc/' '{}' \;
В деталях:
-type f
означает только искать файлы, а не каталоги-name '*.abc'
означает означает, что совпадают только имена файлов, которые заканчиваются на.abc- Обратные слеши после
-type
а также-name
являются символом продолжения строки bash. Я использую их, чтобы сделать этот пример более читабельным, но на практике они не нужны. - Тем не менее, обратная косая черта в конце
-execdir
линия обязательна. Это там, чтобы отделить точку с запятой, которая завершает команду, запускаемую-execdir
, Веселье!
Объяснение регулярного выражения:
s/
начало регулярного выражения\.\/
соответствует ведущему./, который передается в -execdir. Используйте \ для выхода из. и / метасимволы(.+)
соответствовать имени файла. Скобки фиксируют совпадение для последующего использования\.abc
избежать точки, сопоставить азбуку$
закрепить совпадение в конце строки/
отмечает конец части регулярного выражения "match" и начало части "replace"version1_
добавить этот текст к каждому имени файла$1
ссылается на существующее имя файла, потому что мы захватили его в скобках. Если вы используете несколько наборов скобок в части "match", вы можете ссылаться на них здесь, используя $2, $3 и т. Д..abc
имя нового файла будет заканчиваться на.abc. Нет необходимости экранировать метасимвол точки здесь, в разделе "заменить"/
конец регулярного выражения
До
tree --charset=ascii
|-- a_file.abc
|-- Another.abc
|-- Not_this.def
`-- dir1
`-- nested_file.abc
После
tree --charset=ascii
|-- version1_a_file.abc
|-- version1_Another.abc
|-- Not_this.def
`-- dir1
`-- version1_nested_file.abc
Подсказка: rename
Опция -n полезна. Он выполняет пробный прогон и показывает, какие имена он изменит, но не вносит никаких изменений.
Еще один портативный способ:
find /the/path -depth -type f -name "*.abc" -exec sh -c 'mv -- "$1" "$(dirname "$1")/$(basename "$1" .abc).edefg"' _ '{}' \;
# Rename all *.txt to *.text
for f in *.txt; do
mv -- "$f" "${f%.txt}.text"
done
Также смотрите запись о том, почему вы не должны анализировать ls
,
Редактировать: если вам нужно использовать базовое имя, ваш синтаксис будет:
for f in *.txt; do
mv -- "$f" "$(basename "$f" .txt).text"
done
https://unix.stackexchange.com/questions/19654/changing-extension-to-multiple-files
Переименовать файлы и каталоги с find -execdir | rename
Если вы собираетесь переименовывать как файлы, так и каталоги не просто с помощью суффикса, тогда это хороший шаблон:
PATH="$(echo "$PATH" | sed -E 's/(^|:)[^\/][^:]*//g')" \
find . -depth -execdir rename 's/findme/replaceme/' '{}' \;
Удивительным -execdir
вариант делает cd
в каталог перед выполнением rename
команда, в отличие от -exec
,
-depth
убедитесь, что переименование происходит сначала для детей, а затем для родителей, чтобы предотвратить потенциальные проблемы с отсутствующими родительскими каталогами.
-execdir
требуется, потому что переименование плохо работает с путями ввода неосновного имени, например, следующие ошибки:
rename 's/findme/replaceme/g' acc/acc
PATH
взлом требуется, потому что -execdir
имеет один очень раздражающий недостаток: find
крайне самоуверенный и отказывается что-либо делать с -execdir
если у вас есть относительные пути в вашем PATH
переменная окружения, например ./node_modules/.bin
с ошибкой:
find: относительный путь './node_modules/.bin' включен в переменную среды PATH, которая небезопасна в сочетании с действием -execdir команды find. Пожалуйста, удалите эту запись из $PATH
-execdir
является расширением GNU find для POSIX. rename
на основе Perl и происходит от rename
пакет. Проверено в Ubuntu 18.10.
Переименовать обходное решение
Если ваши входные пути не приходят из find
или, если вам уже надоело относительное раздражение пути, мы можем использовать некоторые средства просмотра Perl для безопасного переименования каталогов, как в:
git ls-files | sort -r | xargs rename 's/findme(?!.*\/)\/?$/replaceme/g' '{}'
Я не нашел удобного аналога для -execdir
с xargs
: https://superuser.com/questions/893890/xargs-change-working-directory-to-file-path-before-executing/915686
sort -r
требуется, чтобы файлы следовали за соответствующими каталогами, поскольку более длинные пути идут после более коротких с тем же префиксом.
Это то, что я делал и работал довольно так, как я хотел. Я использовал mv
команда. У меня было несколько .3gp
файлы, и я хотел переименовать их все .mp4
Вот короткий вкладыш для этого:
for i in *.3gp; do mv -- "$i" "ren-$i.mp4"; done
Который просто просматривает текущий каталог, выбирает все файлы.3gp, затем переименовывает (используя mv) в ren-name_of_file.mp4
Я нашел простой способ добиться этого. Чтобы изменить расширения многих файлов с jpg на pdf, используйте:
for file in /path/to; do mv $file $(basename -s jpg $file)pdf ; done