Как рекурсивно изменить расширение нескольких файлов из командной строки?

У меня много файлов с .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

Я бы использовал команду mmv из одноименного пакета:

mmv ';*.abc' '#1#2.edefg'

; соответствует нулю или более */ и соответствует #1 в замену. * соответствует #2, Не рекурсивная версия будет

mmv '*.abc' '#1.edefg'

Переименовать файлы и каталоги с 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" небезопасно для каталога, который находится в переменной 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
Другие вопросы по тегам