У меня есть много файлов с расширением .abc
, и я хочу изменить их на .edefg
Как это сделать из командной строки?
У меня есть корневая папка с большим папки, поэтому решение должно работать рекурсивно.
Если все файлы находятся в одной папке
rename 's/.abc$/.edefg/' *.abc
Для рекурсивного переименования файлов используйте следующее:
find /path/to/root/folder -type f -name '*.abc' -print0 | xargs -0 rename 's/.abc$/.edefg/'
Одна из проблем с рекурсивными переименованиями заключается в том, что какой бы метод вы ни использовали для поиска файлов, он передает весь путь к переименованию
, а не только к имени файла. Это затрудняет выполнение сложных переименований во вложенных папках.
Я использую для решения этой проблемы действие
-execdir
. Если вместо -execdir
использовать -execdir
, то указанная команда выполняется из поддиректории, содержащей соответствующий файл. Таким образом, вместо передачи всего пути к -переименованию
, передается только ./имя_фильмена
. Это значительно облегчает запись регекса.
find /the/path -type f \
-name '*.abc' \
-execdir rename 's/\.\/(.+)\.abc$/version1_$1.abc/' '{}' \;
-тип f означает только поиск файлов, а не каталогов
-имя '*.abc'
означает только совпадающие имена файлов, заканчивающиеся на .abc'{}'
- это плацдарм, который отмечает место, куда -execdir
будет вставлять найденный путь. Требуются одинарные кавычки, чтобы он мог обрабатывать имена файлов с пробелами и символами оболочки.-типа
и -имени
являются символом bash line-continuation. Я использую их, чтобы сделать этот пример более читабельным, но они не нужны, если вы поместите все команды в одну строку.-execdir
необходим. Это необходимо для того, чтобы избежать точки с запятой, которая завершает команду, запущенную с помощью -execdir
. Забавно!-объяснение регекса:
s/
начало регекса\.\/
совпадает с ведущим ./ который -execdir передаёт. Используйте \ для избежания . и / метасимволов (примечание: эта часть зависит от вашей версии find
). См. комментарий пользователя @apollo) (.+)
соответствуют имени файла. В скобках указано соответствие для последующего использования\. abc
escape the dot, match the abc$
anchor the match at the end of the string
/
marks the end of the "match" part of the regex, а начало "replace" part
version1_
add this text to every file name
$1
references the existing filename, because we capture it with parentheses. Если вы используете несколько наборов скобок в части "match", вы можете ссылаться на них здесь, используя $2, $3 и т.д..abc
, то новое имя файла будет заканчиваться на .abc. Здесь нет необходимости экранировать метасимвол точки в разделе "replace"/
конца регексаBefore
tree --charset=ascii
|-- a_file.abc
|-- Another.abc
|-- Not_this.def
`-- dir1
`-- nested_file.abc
After
tree --charset=ascii
|-- version1_a_file.abc
|-- version1_Another.abc
|-- Not_this.def
`-- dir1
`-- version1_nested_file.abc
Hint: rename
's -n полезна опция: rename
's -n. Она делает пробный запуск и показывает, какие имена она изменит, но не вносит никаких изменений.
Портативный способ (который будет работать на любой 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, который можно комбинировать с глобусом или 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
Я бы использовал команду mmv из одноименного пакета :
mmv ';*.abc' '#1#2.edefg'
;
соответствует нулю или более * /
и соответствует # 1
в замена. *
соответствует # 2
. Нерекурсивная версия была бы
mmv '*.abc' '#1.edefg'
Другой переносимый способ:
find /the/path -depth -type f -name "*.abc" -exec sh -c 'mv -- "$1" "$(dirname "$1")/$(basename "$1" .abc).edefg"' _ '{}' \;
Это то, что я сделал и работал так, как я хотел. Я использовал команду 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
Переименуйте файлы и каталоги с помощью 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 найти. Удалите эту запись из $ PATH
См. Также: Почему использование действия '-execdir' небезопасно для каталога, который находится в PATH?
-execdir
является расширением GNU find в POSIX . rename
основан на Perl и происходит из пакета rename
. Протестировано в Ubuntu 18.10.
Переименовать обходной путь просмотра вперед
Если ваши входные пути не исходят из find
, или если у вас достаточно проблем с относительными путями, мы можем использовать некоторые Perl lookahead для безопасного переименования каталогов, как в:
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-execute / 915686
Сортировка -r
необходима для того, чтобы файлы располагались после соответствующих каталогов, поскольку более длинные пути идут после более короткие с таким же префиксом.
# 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