Использование 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 / replace /, делает его более читаемым. Также он не будет высасывать символы из вашего имени файла, если вы случайно запустите его дважды или что-то в этом роде.

Эдвард Андерсон
источник
1
на моем сервере RHEL синтаксис переименования будет следующим: «переименовать 0000 000 F0000 *»
Дэвид ЛеБауэр
1
Скорее всего, renameэто сама «переименованная» ссылка. т.е. renameбыл "переименован" из prename... например, в Ubuntu: readlink -f $(which rename)output /usr/bin/prename... renameУпомянутая Дэвидом программа совершенно другая.
Peter,
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
призрачная собака74
источник
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. кредиты для mahesh здесь: http://www.debian-administration.org/articles/150

Майк
источник
3
Вы должны правильно указать переменные интерполяции; mv "$i" "${i/F00001/F0001}". Но +1
tripleee 01
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

и так далее.

Paxdiablo
источник
1
Это хорошее объяснение, но может быть полезно указать, как вы используете команду sed с другими командами для фактического переименования файлов. Например:ls | sed "s/\(.\).\(.*\)/mv & \1\2/" | bash
jcarballo
@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
призрачная собака74
источник
Ужасный! при условии выполнения произвольного кода (возможно, не в конкретном контексте вопроса, но есть много людей, которые видят подобные ответы и пытаются случайным образом ввести что-то похожее, и это пугает опасно!). Я хотел бы, чтобы вы удалили этот ответ (кроме того, у вас есть еще один хороший ответ, который я поддержал).
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

Старая автономная версия может быть найдена здесь


человек переименовать


Этот инструмент был изначально написан Ларри Уоллом, отцом Perl.

Жиль Кено
источник
-1
for i in *; do mv $i $(echo $i|sed 's/AAA/BBB/'); done
user3164360
источник
4
Добро пожаловать в SO. Пожалуйста, подумайте о добавлении объяснения вашего кода. Это поможет другим пользователям понять это.
Digvijay S
Этот ответ хорош, но это почти дублирующий ответ вышеупомянутого ответа, получившего высокую оценку.
Эрик Лещинский