Використання sed для масового перейменування файлів


87

Об’єктивна

Змініть ці імена файлів:

  • F00001-0708-RG-бяслюйда
  • F00001-0708-CS-akgdlaul
  • F00001-0708-VF-hioulgigl

до цих імен файлів:

  • F0001-0708-RG-бяслюйда
  • F0001-0708-CS-akgdlaul
  • F0001-0708-VF-hioulgigl

Код оболонки

Тестувати:

ls F00001-0708-*|sed 's/\(.\).\(.*\)/mv & \1\2/'

Виконувати:

ls F00001-0708-*|sed 's/\(.\).\(.*\)/mv & \1\2/' | sh

Моє запитання

Я не розумію код sed. Я розумію, що таке команда заміни

$ sed 's/something/mv'

засоби. І я дещо розумію регулярні вирази. Але я не розумію, що тут відбувається:

\(.\).\(.*\)

або тут:

& \1\2/

Для мене перший, здається, просто означає, що це означає: "один символ, за яким слідує один символ, за яким слід будь-яка послідовність довжини одного символу" - але, безумовно, у ньому є не тільки це. Що стосується останньої частини:

& \1\2/

Я поняття не маю.


Відповіді:


149

По-перше, я повинен сказати, що найпростіший спосіб зробити це - використовувати команди prename або rename.

В Ubuntu, OSX (пакет Homebrew, пакет renameMacPorts p5-file-rename) або інших системах із перейменованням perl (попереднє ім'я):

rename s/0000/000/ F0000*

або на системах із перейменованням з util-linux-ng, таких як RHEL:

rename 0000 000 F0000*

Це набагато зрозуміліше, ніж еквівалентна команда sed.

Що стосується розуміння команди sed, корисною є сторінка sed. Якщо запустити man sed і шукати & (за допомогою команди / для пошуку), ви побачите, що це спеціальний символ у s / foo / bar / replacements.

  s/regexp/replacement/
         Attempt  to match regexp against the pattern space.  If success‐
         ful,  replace  that  portion  matched  with  replacement.    The
         replacement may contain the special character & to refer to that
         portion of the pattern space  which  matched,  and  the  special
         escapes  \1  through  \9  to refer to the corresponding matching
         sub-expressions in the regexp.

Отже, \(.\)відповідає першому символу, на який можна посилатися \1. Потім .відповідає наступному символу, який завжди дорівнює 0. Потім \(.*\)відповідає решті імені файлу, на яке можна посилатися \2.

Рядок заміни складає все разом, використовуючи &(оригінальне ім'я файлу), і \1\2це кожна частина імені файлу, крім 2-го символу, який був 0.

Це досить загадковий спосіб зробити це, ІМХО. Якщо з якихось причин команда перейменування була недоступна, і ви хотіли використовувати sed для перейменування (або, можливо, ви робили щось занадто складне для перейменування?), Точніше вираження у вашому регулярному виразі зробило б його набагато зручнішим для читання. Можливо щось на зразок:

ls F00001-0708-*|sed 's/F0000\(.*\)/mv & F000\1/' | sh

Можливість побачити, що насправді змінюється в s / search / replacement /, робить його набагато зручнішим для читання. Крім того, він не буде висмоктувати символи з вашого імені файлу, якщо ви випадково запустите його двічі або щось інше.


1
на моєму сервері RHEL синтаксис перейменування буде "перейменувати 0000 000 F0000 *"
Девід Лебауер,

1
Найімовірніше, renameце саме "перейменоване" посилання. тобто renameбув «перейменований в " з prename.. наприклад, в Ubuntu: readlink -f $(which rename)виходи /usr/bin/prename... The renameзгаданих Давида є іншою програмою повністю.
Пітер.

1
Гарна думка, Пітере. Я оновив відповідь, щоб звернутися до обох утиліт перейменування.
Едвард Андерсон,

3
Щоб налагодити це, у кінці видаліть трубу в sh. Команди лунатимуть на екрані.
Бен Метьюз

1
Ви впевнені, що це хороша порада для передачі випадкових даних sh? це потенційно небезпечно, оскільки може виконуватися довільний код (ви обробляєте дані як код).
gniourf_gniourf

44

у вас було своє пояснення sed, тепер ви можете використовувати лише оболонку, зовнішні команди не потрібні

for file in F0000*
do
    echo mv "$file" "${file/#F0000/F000}"
    # ${file/#F0000/F000} means replace the pattern that starts at beginning of string
done

1
Гарно, але ви не можете робити посилання з дужками.
Леонідас Цампрос

26

Я написав невеликий допис із прикладами щодо пакетного перейменування, використовуючи sedпару років тому:

http://www.guyrutenberg.com/2009/01/12/batch-renaming-using-sed/

Наприклад:

for i in *; do
  mv "$i" "`echo $i | sed "s/regex/replace_text/"`";
done

Якщо регулярний вираз містить групи (наприклад \(subregex\) , то ви можете використовувати їх в тексті заміни , як \1\, і \2т.д.


Зауважте, що відповіді лише на посилання не рекомендуються (посилання з часом старіють). Будь ласка, відредагуйте свою відповідь та додайте сюди конспект.
kleopatra

не настільки ефективно, але виконує роботу над кількома сотнями файлів. Прихильний.
Варун Чандак,

21

Найпростіший спосіб:

for i in F00001*; do mv "$i" "${i/F00001/F0001}"; done

або, портативно,

for i in F00001*; do mv "$i" "F0001${i#F00001}"; done

Це замінює F00001префікс в іменах файлів на F0001. кредити Махешу тут: http://www.debian-administration.org/articles/150


3
Ви повинні правильно вказати інтерполяції змінних; mv "$i" "${i/F00001/F0001}". Але +1
триплеє

7

sedкоманда

s/\(.\).\(.*\)/mv & \1\2/

засоби для заміни:

\(.\).\(.*\)

з:

mv & \1\2

як звичайна sedкоманда. Однак дужки &та \nмаркери трохи змінюють це.

Рядок пошуку відповідає (і запам’ятовує як зразок 1) одному символу на початку, за яким слідує один символ, перекритий рештою рядка (запам'ятовується як зразок 2).

У рядку заміни ви можете посилатися на ці узгоджені шаблони, щоб використовувати їх як частину заміни. Ви також можете назвати всю відповідну частину як &.

Отже, що робить ця sedкоманда, це створення mvкоманди на основі вихідного файлу (для джерела) та символів 1 та 3, ефективно видаляючи символ 2 (для пункту призначення). Це дасть вам ряд рядків у наступному форматі:

mv F00001-0708-RG-biasliuyda F0001-0708-RG-biasliuyda
mv abcdef acdef

і так далі.


1
Це було гарне пояснення, але може бути корисно вказати, як ви використовуєте команду sed разом з іншими командами, щоб насправді перейменувати файли. Наприклад:ls | sed "s/\(.\).\(.*\)/mv & \1\2/" | bash
jcarballo 04.03.14

@jcarballo: небезпечно проводити синтаксичний аналіз ls, прокладати через, sedа потім проводити через оболонку! він підлягає довільному виконанню коду з підробленими іменами файлів. Проблема полягає в тому, що дані слід розглядати як дані, і тут вони зазвичай серіалізуються в код без будь-яких запобіжних заходів. Я хотів би, щоб paxdiablo міг видалити цю відповідь, оскільки вона насправді не показує належної практики. (Я наткнувся на це питання, тому що новачок випадковим чином | shпровів трубу після команди, яка не спрацювала, і побачивши це запитання та відповіді, подумав, що це буде працювати краще - я в жаху!) :).
gniourf_gniourf

3

Матеріал із зворотною косою рискою означає: "під час узгодження шаблону тримайтеся за речі, які тут відповідають" Пізніше, на стороні заміщення тексту, ви зможете повернути ці запам’ятані фрагменти назад за допомогою "\ 1" (перший блок у дужках), "\ 2" (другий блок) тощо.


1

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

s/.//2

але ваша команда будує mvкоманду і передає її до оболонки для виконання.

Це не більше для читання, ніж ваша версія:

find -type f | sed -n 'h;s/.//4;x;s/^/mv /;G;s/\n/ /g;p' | sh

Четвертий символ видаляється, оскільки перед findкожною назвою файлу додається "./".


Я хотів би, щоб Ви могли видалити цю відповідь. Хоча це, можливо, було добре в дуже конкретному випадку OP, багато людей бачать такі відповіді і не розуміють їх, і випадковим чином передають | shкоманду, яка не працює, в надії, що вона спрацює краще. Це жахливо! (і крім того, це погана практика). Сподіваюся, ви зрозумієте!
gniourf_gniourf

0

Дужки фіксують певні рядки для використання зворотними косими числами.


0
 ls F00001-0708-*|sed 's|^F0000\(.*\)|mv & F000\1|' | bash

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

0

Ось що я б зробив:

for file in *.[Jj][Pp][Gg] ;do 
    echo mv -vi \"$file\" `jhead $file|
                           grep Date|
                           cut -b 16-|
                           sed -e 's/:/-/g' -e 's/ /_/g' -e 's/$/.jpg/g'` ;
done

Тоді, якщо це виглядає нормально, додайте | shдо кінця. Тому:

for file in *.[Jj][Pp][Gg] ;do 
    echo mv -vi \"$file\" `jhead $file|
                           grep Date|
                           cut -b 16-|
                           sed -e 's/:/-/g' -e 's/ /_/g' -e 's/$/.jpg/g'` ;
done | sh

0

Використання перейменування perl ( обов’язково в панелі інструментів):

rename -n 's/0000/000/' F0000*

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

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

Команда перейменувати, яка є частиною util-linuxпакету, не буде.

Якщо виконати таку команду ( GNU)

$ rename

і бачите perlexpr, тоді це, здається, правильний інструмент.

Якщо ні, то зробити це за замовчуванням (як правило, це вже має місце) для Debianта похідного типу Ubuntu:

$ sudo apt install rename
$ sudo update-alternatives --set rename /usr/bin/file-rename

Для Archlinux:

pacman -S perl-rename

Для дистрибутивів сімейства RedHat:

yum install prename

Пакет 'prename' знаходиться у сховищі EPEL .


Для Gentoo:

emerge dev-perl/rename

Для * BSD:

pkg install gprename

або p5-File-Rename


Для користувачів Mac:

brew install rename

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

cpan -i File::Rename

Стару автономну версію можна знайти тут


перейменувати людину


Цей інструмент був спочатку написаний Ларрі Уоллом, татом Перла.


-1
for i in *; do mv $i $(echo $i|sed 's/AAA/BBB/'); done

4
Ласкаво просимо до SO. Будь ласка, додайте пояснення свого коду. Це допоможе іншим користувачам зрозуміти це.
Digvijay S

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