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


73

У мене є багато файлів з .abcрозширенням і хочу змінити їх на .edefg
Як це зробити з командного рядка?

У мене є коренева папка з багатьма підпапками, тому рішення має працювати рекурсивно.


Відповіді:


85

Портативний спосіб (який буде працювати в будь-якій системі, сумісній з 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


перший просто додає нове розширення до старого, воно не замінює ...
user2757729

3
@ user2757729, він замінює його. "${1%.abc}"розширюється до значення, $1крім без .abcкінця. Докладніше про маніпуляції з рядковими оболонками див. У faq 100 .
гейрха

Я запустив це на файлі файлів ~ 70k файлів ... принаймні, на моїй оболонці він, безумовно, не замінював її.
user2757729

1
@ user2757729 Ви, ймовірно, залишили "abc" в${1%.abc}...
kvnn

1
Якщо хтось інший цікавить, що підкреслення робить у першій команді, це потрібно, тому що при sh -c першому аргументі призначається $ 0, а будь-які інші аргументи призначаються позиційним параметрам . Отже, _аргумент є фіктивним аргументом, а "{}" - першим позиційним аргументом sh -c.
hellobenallan

48

Це зробить необхідне завдання, якщо всі файли знаходяться в одній папці

rename 's/.abc$/.edefg/' *.abc

Для перейменування файлів рекурсивно використовуйте це:

find /path/to/root/folder -type f -name '*.abc' -print0 | xargs -0 rename 's/.abc$/.edefg/'

8
Або rename 's/.abc$/.edefg/' /path/to/root/folder/**/*.abcв сучасній версії Bash.
Адам Біртек

Велике спасибі Адаму, що дав мені підказку щодо використання * .abc у папках рекурсивно!
Rafał Cieślak

Чудова порада, дякую! Де я можу знайти додаткову документацію про маленький фрагмент синтаксису regex? Як, що це sна початку, і які інші варіанти я можу використовувати там. Дякую !
Анто

@AdamByrtek Я просто не зміг із вашою пропозицією щодо Utopic. ("немає таких файлів" чи якісь такі.)
Джонатан Ю.

14

Одна з проблем рекурсивних перейменувань полягає в тому, що який би метод ви не використовували для пошуку файлів, він проходить весь шлях 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
  • '{}'- це заповнювач, який позначає місце, куди -execdirбуде вставлено знайдений шлях. Одиночні лапки необхідні, щоб вони могли обробляти імена файлів з пробілами та символами оболонки.
  • Нахили нахилу після -typeі -nameє символом продовження баш-лінії. Я використовую їх, щоб зробити цей приклад більш читабельним, але вони не потрібні, якщо ви поставите свою команду в один рядок.
  • Однак нахил в кінці -execdirрядка потрібен. Саме там можна уникнути крапки з комою, яка закінчує команду, яку виконує -execdir. Весело!

Пояснення регулярного вираження:

  • s/ початок регулярного виразка
  • \.\/відповідати ведучому ./, який передає -execdir. Використовуйте \, щоб уникнути. та / метахарактеристики (зверніть увагу: ця частина залежить від вашої версії find. Див. коментар користувача @apollo)
  • (.+)відповідати імені файлу. Дужки фіксують збіг для подальшого використання
  • \.abc втекти крапкою, відповідати абс
  • $ закріпіть сірник в кінці рядка

  • / позначає закінчення «збіжної» частини регулярного вираження та початок частини «заміни»

  • 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's -n варіант корисний. Це робить сухий пробіг і показує, які імена він змінить, але не вносить жодних змін.


Дякую за пояснення докладно, але як не дивно, коли я намагаюся це, --execdirне проходив у ведучому, ./тому \.\/частина в регулярному виразі можна опустити. Може, це тому, що я на OSX не ubuntu
apollo

5

Ще один портативний спосіб:

find /the/path -depth -type f -name "*.abc" -exec sh -c 'mv -- "$1" "$(dirname "$1")/$(basename "$1" .abc).edefg"' _ '{}' \;

4
Ласкаво просимо до Ask Ubuntu! Хоча це цінна відповідь, я рекомендую розширити її (редагуючи), щоб пояснити, як і чому працює ця команда.
Елія Каган

3
# 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


0

Це те, що я робив і працював досить просто так, як хотів. Я використав mvкоманду. У мене було кілька .3gpфайлів, і я хотів перейменувати їх на всі.mp4

Ось такий короткий ліній:

for i in *.3gp; do mv -- "$i" "ren-$i.mp4"; done

Який просто сканує через поточний каталог, збирає всі .3gp файли, а потім перейменовує (використовуючи mv) у ren-name_of_file.mp4


Це буде перейменовано file.3gpна file.3gp.mp4, що може бути не тим, чого хочуть більшість людей.
муру

@muru, але принцип все ще існує. Просто виберіть будь-яке розширення, а потім вкажіть розширення призначення, щоб їх перейменовано. Ось .3gpабо .mp4тут були лише для ілюстрації.
Хофі

Синтаксис є кращим, однолінійним і зрозумілим. Однак я б використав basename "$i" .mp4для видалення попереднього розширення замість "ren- $ i.mp4".
TaoPR

Правда. Влучне зауваження.
Хофі

0

Я знайшов простий спосіб досягти цього. Щоб змінити розширення багатьох файлів з jpg в pdf, використовуйте:

for file in /path/to; do mv $file $(basename -s jpg $file)pdf ; done

0

Я б використовував команду mmv з однойменного пакету :

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

Ці ;матчі нуль або більше , */і відповідає #1в заміні. У *відповідає до #2. Нерекурсивна версія була б

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

0

Перейменуйте файли та каталоги за допомогою 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 до 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Потрібно для того , щоб файли приходять після їх відповідних каталогів, так як більш довгі шляхи приходять після того, як більш короткі, з тим же префіксом.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.