Перейменування великої кількості файлів зображень за допомогою bash


16

Мені потрібно перейменувати бл. 70 000 файлів. Наприклад: Від sb_606_HBO_DPM_0089000і sb_606_dpm_0089000т.д.

Діапазон чисел переходить від 0089000до 0163022. Це лише перша частина імені, яку потрібно змінити. всі файли знаходяться в одному каталозі і нумеруються послідовно (послідовність зображень). Цифри повинні залишатися незмінними.

Коли я пробую це в башті, він мене гризе, що "Аргумент список занадто довгий".

Редагувати:

Я спершу спробував перейменувати один файл за допомогою mv:

mv sb_606_HBO_DPM_0089000.dpx sb_606_dpm_0089000.dpx

Потім я спробував перейменувати діапазон (на минулому тижні я дізнався, як переміщувати завантаження файлів, тому подумав, що той самий синтаксис може працювати для перейменування файлів ...). Я думаю, я спробував таке (або щось подібне):

mv sb_606_HBO_DPM_0{089000..163023}.dpx sb_606_dpm_0{089000..163023}.dpx

4
Рецензентам : я не думаю, що це дублікат; більшість відповідей CLI на інше питання тут не працюватиме через велику кількість файлів, що стикаються з ARG_MAXобмеженням оболонки . Оскільки це запитання прямо вимагає рішення командного рядка, (можливо, рівне) рішення GUI, як і в іншому питанні, також не відповідають.
десерт

1
Я не думаю, що це обман, тому що нормально мати кілька запитань щодо перейменування файлів. Будь ласка, не давайте закривати конкретні запитання щодо загальних ресурсів, які насправді не відповідають на них ...
Zanna

1
@rich Якщо ви можете чітко відредагувати, яку команду ви пробували, то було б зрозуміліше, що це не дура. (Це показує нам, що ви знаєте про такий підхід.)
Спархаук

2
багате, ваше запитання не є дурним, тому що це специфічне питання. Не хвилюйтеся з цього приводу. Що ще важливіше, після того, як на запитання надійшла низка оновлених відповідей, його редагування, ймовірно, не є гарною ідеєю, оскільки ваші зміни можуть зробити існуючі відповіді менш валідними. Тепер я відчуваю, що моя відповідь повинна пояснити, чому mv {1..2} {3..4}це не працює, що зовсім інша проблема, ніж ARG_MAX... Усі інші, хто відповів, напевно, відчують те саме! Тож, з моєї точки зору, я б хотів, щоб ви відмовилися від останньої редакції, і, якщо хочете, задати зовсім нове запитання про mving з діапазонами
Zanna

1
@Sparhawk з першої версії запитання ОП написав досить чітко, що проблема - argument list too longпомилка. Далі не потрібно уточнювати, це очевидно не є обов язковим, оскільки нам потрібен спосіб вирішення проблеми ARG_MAX, і відповіді у запропонованому дублікаті цього не роблять.
тердон

Відповіді:


25

Один з способів полягає у використанні findз -exec, і +варіант. Це створює список аргументів, але розбиває його на стільки викликів, скільки потрібно для роботи з усіма файлами, не перевищуючи максимальний список аргументів. Він підходить, коли всі аргументи будуть розглядатися однаково. Це справа з rename, хоча і не з mv.

Можливо, вам доведеться встановити перейменування Perl:

sudo apt install rename

Потім ви можете використовувати, наприклад:

find . -maxdepth 1 -exec rename -n 's/_HBO_DPM_/_dpm_/' {} +

Видаліть -nпісля тестування, щоб фактично перейменувати файли.


11

Я пропоную три варіанти. Кожна команда є простою однорядковою командою, але я запропоную варіанти для більш складних випадків, головним чином, якщо файли, які обробляються, змішані з іншими файлами в тій же дирекції.

ммв

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

mmv '*HBO_DPM*' '#1dpm#2'

Зауважте, що аргументи передаються у вигляді рядків, тому розширення глобального типу не відбувається в оболонці. Команда отримує рівно два аргументи, а потім знаходить відповідні файли внутрішньо, без жорстких обмежень щодо кількості файлів. Також зауважте, що команда вище передбачає, що всі файли, які відповідають першому глобусу, повинні бути перейменовані. Звичайно, ви можете бути більш конкретними:

mmv 'sb_606_HBO_DPM_*' 'sb_606_dpm_#1'

Якщо у вас є файли поза запитуваним діапазоном чисел у тому самому каталозі, можливо, вам буде краще пройти цикл за номерами, наведеними далі у цій відповіді. Однак ви також можете використовувати послідовність викликів mmv з відповідними шаблонами:

mmv 'sb_606_HBO_DPM_0089*'       'sb_606_dpm_0089#1'    # 0089000-0089999
mmv 'sb_606_HBO_DPM_009*'        'sb_606_dpm_009#1'     # 0090000-0099999
mmv 'sb_606_HBO_DPM_01[0-5]*'    'sb_606_dpm_01#1#2'    # 0100000-0159999
mmv 'sb_606_HBO_DPM_016[0-2]*'   'sb_606_dpm_016#1#2'   # 0160000-0162999
mmv 'sb_606_HBO_DPM_01630[01]?'  'sb_606_dpm_01630#1#2' # 0163000-0163019
mmv 'sb_606_HBO_DPM_016302[0-2]' 'sb_606_dpm_016302#1'  # 0163020-0163022

петля над числами

Якщо ви хочете нічого не встановлювати, або вам потрібно вибрати номер діапазону, уникаючи збігів поза цим діапазоном, і ви готові чекати 74,023 викликів команд, ви можете використовувати звичайний цикл bash:

for i in {0089000..0163022}; do mv sb_606_HBO_DPM_$i sb_606_dpm_$i; done

Тут це особливо добре працює, оскільки в послідовності немає прогалин. В іншому випадку ви можете перевірити, чи існує вихідний файл.

for i in {0089000..0163022}; do
  test -e sb_606_HBO_DPM_$i && mv sb_606_HBO_DPM_$i sb_606_dpm_$i
done

Зауважте, на відміну від for ((i=89000; i<=163022; ++i)) від розширення дужок все-таки обробляють провідні нулі з часу випуску Bash пару років тому. Насправді я вимагав зміни, тому я радий бачити випадки використання.

Подальше читання: Розширення дужок на інформаційних сторінках Bash, зокрема частині про{x..y[..incr]} .

петля над файлами

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

for i in *HBO_DPM*; do mv "$i" "${i/HBO_DPM/dpm}"; done

Знову це одне mv виклик на файл. І знову цикл перебуває над довгим списком елементів, але весь список не передається як аргумент підпроцесу, а обробляється внутрішньо bash, тому обмеження не створюватиме вам проблем.

Подальше читання: Розширення параметрів оболонки на інформаційних сторінках Bash, документування${parameter/pattern/string} серед інших.

Якщо ви хочете обмежити діапазон номерів до вказаного вами, ви можете додати чек для цього:

for i in sb_606_HBO_DPM_+([0-9]); do
  if [[ "${i##*_*(0)}" -ge 89000 ]] && [[ "${i##*_*(0)}" -le 163022 ]]; then
    mv "$i" "${i/HBO_DPM/dpm}"
  fi
done

Тут ${i##pattern}видаляє найдовший префікс зіставлення patternз $i. Цей найдовший префікс визначається як що-небудь, то підкреслення, то нуль або більше нулів. Останнє записується як *(0)розширений глобусний шаблон, який залежить від встановленого extglobваріанту . Видалення провідних нулів важливо, щоб число було розглянуто як базу 10, а не базу 8. +([0-9])Аргумент циклу - це ще один розширений глобул, що відповідає одній або більше цифр, на випадок, якщо у вас є файли, які починаються однаково, але не закінчуються на число.


Дякую! Це спрацювало як мрія: бо я в {0089000..0163022}; do mv sb_606_HBO_DPM_ $ i sb_606_dpm_ $ i; зроблено - мені довелося додати розширення імені файлу, щоб він працював, але це робив саме те, що я хотів, і я навіть розумію синтаксис. Дякую @MvG
багатий

@rich: Щасливий, що можу допомогти - вам і, сподіваємось, і майбутнім відвідувачам. Не забудьте прийняти найкориснішу відповідь. Ви завжди можете змінити цю галочку в майбутньому, якщо вийде щось краще.
MvG

10

Один із способів обійти ARG_MAXмежу - використовувати вбудовану оболонку bash printf:

printf '%s\0' sb_* | xargs -0 rename -n 's/HBO_DPM/dpm/'

Вих.

rename -n 's/HBO_DPM/dpm/' sb_*
bash: /usr/bin/rename: Argument list too long

але

printf '%s\0' sb_* | xargs -0 rename -n 's/HBO_DPM/dpm/'
rename(sb_606_HBO_DPM_0089000, sb_606_dpm_0089000)
.
.
.
rename(sb_606_HBO_DPM_0163022, sb_606_dpm_0163022)

7
find . -type f -exec bash -c 'echo $1 ${1/HBO_DPM/dpm}' _ {} \;
./sb_606_HBO_DPM_0089000 ./sb_606_dpm_0089000

findв поточній директорії .для всіх файлів -type fі зробити перейменувати файл знайдений $1з заміною HBO_DPMз dmp по одному-exec ... \;

замінити echoз mvвиконати перейменування.


6

Ви можете написати невеликий сценарій python, щось на зразок:

import os
for file in os.listdir("."):
    os.rename(file, file.replace("HBO_DPM", "dpm"))

Збережіть це як текстовий файл, як rename.pyу папці, у якій знаходяться файли, а потім із терміналом у цій папці перейдіть:

python rename.py

6

Ви можете це зробити файл за файлом (це може зайняти деякий час) за допомогою

sudo apt install util-linux  # if you don't have it already
for i in *; do rename.ul HBO_DPM dpm "$i"; done

Як і Perl, який renameвикористовується в інших відповідях, rename.ulтакож є варіант -nабо --no-actдля тестування.


Я відредагував ваш коментар щодо відповіді Занні, будь ласка, відредагуйте відповідь Занні або залиште коментар.
fosslinux

@ubashu це не коментував мою відповідь - він посилався на -nпрапор, який я використовував для тестування, і пропонував його також використовувати rename.ul.
Занна

3

Я бачу, що мого найкращого друга sedна вечірку ніхто не запрошував :). Наступна forпетля здійснить вашу мету:

for i in sb_606_HBO_DPM*; do
  mv "$i" "$(echo $i | sed 's/HBO_DPM/dpm/')";
done

Для такої роботи є багато інструментів, виберіть той, який для вас найбільш зрозумілий. Цей простий і легко змінюється відповідно до тих чи інших цілей ...


Надано, що не дуже актуально в цьому конкретному випадку, але це не вдасться, якщо будь-яке з назв файлів містить нові рядки. Я згадую про це, оскільки більшість (усіх?) Інших відповідей є надійними і можуть мати справу з довільними іменами файлів або лише працювати над схемою іменування файлів ОП.
тердон

... нові рядки, пробіли, підмітні знаки, ... деяких з них можна уникнути, цитуючи $iв підстановці команд, але не простий спосіб обробити останній рядок у назві файлу.
муру

3

Оскільки ми надаємо варіанти, ось підхід Perl. cdв цільовий каталог і запустіть:

perl -e 'foreach(glob("sb_*")){rename $_, s/_HBO_DPM_/_dpm_/r}'

Пояснення

  • perl -e : запустіть сценарій, заданий користувачем -e .
  • foreach(glob){} : запустити все, що є в { } кожному з результатів глобуса.
  • glob("sb_*") : повертає список усіх файлів і каталогів у поточному каталозі, імена яких відповідають глобальній оболонці sb* .
  • rename $_, s/_HBO_DPM_/_dpm_/r: перл магія. $_це спеціальна змінна, яка містить кожен елемент, який ми повторюємо (в foreach). Отже, тут буде знайдений кожен файл. s/_HBO_DPM_/_dpm_/замінює перше виникнення _HBO_DPM_с _dpm_. Він працює $_за замовчуванням, тому буде працювати на кожному імені файлу. В /rозначає «застосувати цю заміну копію цільової рядки (ім'я файлу) і повертає модифіковану рядок. renameРобить то , що можна було очікувати: він перейменовує файли Так що все це буде перейменувати ім'я поточного файлу (. $_) До себе з _HBO_DPM_замінено на _dpm_.

Ви можете написати те саме, що і розширений (і більш читабельний сценарій):

#! /usr/bin/env perl
use strict;
use warnings;

foreach my $fileName (glob("sb_*")){
  ## Copy the name to a new variable
  my $newName = $fileName;
  ## change the copy. $newName is now the changed version
  $newName =~ s/_HBO_DPM_/_dpm_/;
  ## rename
  rename $fileName, $newName;
}

1

Залежно від виду перейменування, який ви передбачаєте, використання vidir із редагуванням кількох рядків може бути задовільним.
У вашому конкретному випадку ви можете вибрати всі рядки у текстовому редакторі та видалити _ " HBO" частину імен файлів за кілька натискань клавіш.


так, vi має глянцевий пошук та заміну.
Ясен

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