Инструмент командной строки rename — это мощное средство для одновременного переименования или даже перемещения нескольких файлов по заданному шаблону.
Однажды, разбираясь с возникшей проблемой не сервере, я скачал папку с логами на свой домашний компьютер. Очень скоро я понял, что читать логи предпочтительнее по дате, для чего мне надо было переименовать все лог-файлы в «правильном» формате. Другой пример. Мой товарищ увлекается фотографированием, и ему каждый раз, после отбора удачных снимков, приходится заниматься переименованием более сотен фотографий.
С изменением имён нескольких файлов можно справиться вручную, но изменение имён более десятка файлов быстро становится не только утомительным, но и чреватым допусками ошибок. В Linux есть несколько инструментов, которые позволяют переименовывать файлы в массовом порядке. В частности, файловый менеджер Thunar имеет очень гибкий инструмент Bulk Rename, с несколькими мощными встроенными критериями сопоставления с образцом, из которых можно выбирать, что делает инструмент достаточным для большинства случаев использования.
Однако, после того, как вы попробуете групповое переименование файлов с помощью командной строки, вы поймёте, что процесс проходит быстрее, чем с помощью графического инструмента. Кроме того, инструмент Bulk Rename в Thunar хоть и мощный, но все же ограничен в своей гибкости. К примеру, Bulk Rename может переименовывать файлы, но не может перемещать файлы из одного каталога или группы каталогов в другой.
В этой статье мы подробно рассмотрим команду rename, мощный инструмент командной строки, написанный на языке Perl, который можно использовать для массового переименования файлов и многого другого.
Начало работы
Установить инструмент командной строки rename на Debian, Ubuntu или их производные дистрибутивы можно командой:
sudo apt install rename
После установки команда rename имеет следующий синтаксис:
rename [options] [expression] [files]
Файлы — это один или несколько файлов для переименования. Как и в других инструментах командной строки, допускаются стандартные подстановочные символы оболочки, такие как *.png или file[0-9].
Выражение состоит из параметров, которые сопоставляют и изменяют части имён файлов. Результаты применения выражения к каждому имени файла используются для присвоения файлу нового имени. Обычно указывается только одна команда — команда s/// для поиска и изменения имён файлов. Команда y/// используется реже, в основном для замены или транслитерации отдельных букв.
Но на самом деле выражением может быть практически любой допустимый код Perl, оперирующий со строками. Если вы интересуетесь выражениями Perl, обратитесь к официальной документации по Perl. Однако маловероятно, что для изменения имён файлов вам понадобится что-то большее, чем команды s/// и y///.
Кроме того, rename принимает одну или несколько опций, наиболее полезные из них приведены в таблице ниже.
Таблица 1. Параметры переименования
Простой пример
Для первого примера я сохранил несколько HTML-файлов со статьями из Википедии. Браузер назвал каждую веб-страницу по её заголовку, которые заканчивались дефисом и словом «Википедия», что является лишним и неоправданно удлиняет имя каждого файла.
ls -N |more
IEEE 802.11n — Википедия.html
Linux — Википедия.html
Квалификаторы типа — Википедия.html
Квантовая вероятность — Википедия.html
Оксид железа(III) — Википедия.html
Чтобы удалить в конце имени каждого файла пробелы, дефис и слово «Википедия», воспользуемся следующей командой:
rename -v 's/ — Википедия\.html$/.html/' *.html
Команда s/// ищет часть имени файла, соответствующую шаблону, который заключён между первыми двумя косыми чертами (фигурная скобка 1 на скриншоте выше). Далее заменяет найденный текст на другой, заключенный между второй и третьей косыми чертами (фигурная скобка 2).
Ниже показан результат выполнения этой команды.
ls -N |more
IEEE 802.11n.html
Linux.html
Квалификаторы типа.html
Квантовая вероятность.html
Оксид железа(III).html
Обратите внимание на символ обратной косой черты «\», предшествующий символу точки «.» в поисковом выражении, который мы использовали выше. Из-за того, что символ точки имеет особое значение в регулярных выражениях, имеет смысл использовать перед ним обратную косую черту (так называемый escape). Если этого не сделать, то символ точки будет соответствовать не только одиночному символу точки в имени файла, но и любому другому символу. В нашем примере, без экранирования точки, rename искал бы соответствие на такие имена как — « — Википедияahtml», « — Википедияzhtml», « — Википедия!html» и так далее.
Конкретно в нашем случае, имена файлов, которые я хочу переименовать, не содержит ничего, кроме «* — Википедия.html», поэтому экранирование символа точки в данном случае излишне. Однако, формулируя поисковые запросы, следует быть как можно более конкретным.
Символ точки — один из нескольких метасимволов, которые имеют особое значение в регулярных выражениях. Знак доллара «$» в конце поискового выражения указывает rename на соответствие части имени файла только в том случае, если совпадение происходит в конце имени файла.
Таблица 2. Метасимволы регулярных выражений
Например, файл «Квантовая вероятность — Википедия.html» будет соответствовать регулярному выражению, которое я использовал ранее. А вот файл «ААА — Википедия.html.gz», который оканчивается на «.gz» не будет соответствовать. Как и в случае с символом точки, для соответствия буквальному символу знака доллара в имени файла перед знаком доллара должен был бы стоять обратный слеш.
Вы также можете указать один или несколько символов, следующих за завершающей косой чертой в команде s///. Эти символы дополнительно изменяют поведение операции поиска и замены, например, отключают сопоставление с учетом регистра. Ниже рассмотрим подробности о параметрах, поддерживаемых командой s///.
Параметры s///
Добавив один или несколько дополнительных символов в конец команды s///, можно изменить поведение операции поиска и замены различными способами. Каждая опция — это один символ. Несколько опций могут быть указаны, если сразу после одного символа опции следует другой. Примеры:
- s/dog/cat/g,
- s/.html$/.HTM/i,
- s/recieve/receive/gi.
Хотя и поддерживается более десятка вариантов опций, но только два из них действительно полезны для большинства пользователей при переименовании файлов. Первая — это «g», указывает rename заменить все вхождения строки поиска строкой замены, а не только первое вхождение. По умолчанию заменяется только первое вхождение поискового термина. В большинстве случаев этого достаточно, но не в том случае, если вы хотите заменить все вхождения, например, слова «аффект» на «эффект» в имени файла «аффект_аффективности_приводит_к_аффекту.txt».
Другая потенциально полезная опция, «i», обеспечивает поиск без учета регистра. Другими словами, rename будет неважно, в верхнем или нижнем регистре находится символ в строке поиска. Любой из этих символов будет соответствовать любому из символов в имени файла. По умолчанию, если символ в строке поиска является строчным, соответствующий символ в имени файла также должен быть строчным, чтобы строка поиска совпала. Например, без параметра «i» поисковый запрос «.html» будет соответствовать файлу «test1.html», но не «test2.HTML» или «test3.Html». Напротив, с параметром «i» то же самое поисковое выражение будет соответствовать всем трём файлам. Даже если все или часть поискового выражения будут написаны с заглавной буквы.
Использование обратных ссылок
Переименование файлов с помощью простых поисковых терминов и регулярных выражений в большинстве случаев является достаточным. Чаще всего достаточно просто добавить или убрать фиксированную строку к имени каждого файла, как в примере с загруженными страницами Википедии.
Но иногда возникает необходимость переименовать файлы более сложными способами. В следующем примере, у меня есть несколько лог-файлов с датами и временем в именах. Имя каждого файла содержит год, месяц, день, час, минуту и секунду, в которые был создан лог-файл, в соответствии с конвенцией ISO 8601, международным форматом дат и времени.
ls -N |more
daemon_20200309_071842
messages_20211213_134327
messages_20230402_093200
syslog_20191013_233611
syslog_20220726_185603
Но предположим, что я хочу, чтобы даты и время были заданы в более удобном для меня формате. Так, в моём случае, я хочу, чтобы в именах файлов между компонентами даты (день.месяц.год) вставлялись точки, а между компонентами времени (часы:минуты:секунды) — двоеточия. Ниже показано, как должны выглядеть имена файлов после их переименования.
ls -N |more
daemon_09.03.2020_07:18:42
messages_13.12.2021_13:43:27
messages_02.04.2023_09:32:00
syslog_13.10.2019_23:36:11
syslog_26.07.2022_18:56:03
Переименование файлов таким образом невозможно осуществить с помощью простого синтаксиса регулярных выражений. Для этого нужно не только искать определённые части имени файла, но и ссылаться в строке замены на совпадающий текст каждой из этих частей. Сначала нужно найти год (четырехзначное число), затем месяц (двузначное число), затем день (ещё одно двузначное число), а затем поменять их местами в соответствии с нужным форматом.
Регулярные выражения позволяют ссылаться на части строки поиска в строке замены с помощью обратных ссылок. Чтобы использовать обратные ссылки, часть строки поиска, на которую нужно сослаться, нужно заключить в круглые скобки. После чего эти части, заключённые в круглые скобки, могут быть отнесены к строке замены, путём вставки в строку замены символа знака доллара «$», за которым следует номер индекса.
Следующая команда rename использует обратные ссылки для выполнения моей первой задачи по изменению порядка следования компонентов дат, а также вставляет точки между компонентами:
rename -v 's/([0-9]{4})([0-9]{2})([0-9]{2})/$3\.$2\.$1/' *
На рисунке ниже показано, на какие части поискового выражения ссылается каждая обратная ссылка. Стрелки на рисунке указывают на области поискового выражения, на которые ссылаются скобки.
Комбинирование нескольких операций
В предыдущем примере были переформатированы только даты. Но мне всё ещё нужно вставить двоеточия между каждым компонентом времени. И снова я могу использовать обратные ссылки, как показано ниже:
rename -v 's/([0-9]{2})([0-9]{2})([0-9]{2})$/$1:$2:$3/' *
Такой вариант тоже имеет право на существование. Но я хочу использовать одну единственную команду rename для изменения даты и времени вместо того, чтобы последовательно выполнять две отдельные команды. Конечно же, я мог бы объединить два поисковых выражения в одно очень длинное поисковое выражение, которое получится громоздким и трудночитаемым:
rename 's/([0-9]{4})([0-9]{2})([0-9]{2})_([0-9]{2})([0-9]{2})([0-9]{2})$/$3\.$2\.$1_$4:$5:$6/' *
К счастью, в rename можно выполнить обе задачи одной командой, но при этом сохранить их логическое разделение. Если каждое выражение разделено точкой с запятой, rename может выполнить два или более выражений в одной команде:
rename -v 's/([0-9]{4})([0-9]{2})([0-9]{2})/$3.$2.$1/;
s/([0-9]{2})([0-9]{2})([0-9]{2})$/$1:$2:$3/' *
Обратите внимание на новую строку после точки с запятой. Хотя это и не обязательно, но улучшает читаемость поискового выражения. Команда rename интерпретирует его как безобидный символ пробела.
Транслитерация символов
Команда y/// транслитерирует текст. Она ищет каждый символ, указанный в первом параметре команды, и заменяет любой его экземпляр на соответствующий символ во втором параметре. Например, чтобы заменить в именах файлов все AZ на ZA, или ZA на AZ , используйте команду:
rename 'y/AZ/ZA/' *
После выполнения этой команды файл «AZGREB.TXT» станет «ZAGREB.TXT».
Хотя команда y/// чувствительна к регистру, как и s///, у команды y/// нет переключателя опций для включения чувствительности к регистру. Таким образом, приведенная выше команда y/// заменит ZAGREB.TXT, но не zagreb.txt. Более того, она заменит Zagreb.txt на Aagreb.txt, но не на Azgreb.txt, как вы могли бы ожидать. Чтобы сделать это, вам нужно изменить команду на:
rename 'y/AZaz/ZAza/' *
Одно из распространенных применений команды y/// — преобразование прописных имен файлов в строчные или наоборот. Это может оказаться полезным для старых файлов MS-DOS или ранних версий Windows, которые сохраняли файлы в верхнем регистре. Можно реализовать такую транслитерацию, явно указав в команде весь алфавит, но это громоздко, поскольку в этом случае придется набрать не менее 52 букв: 26 прописных букв в выражении поиска и 26 строчных букв в выражении замены. Вместо этого можно указать диапазоны символов в поисковом выражении, как в y/[A-Z]/[a-z]/, для замены прописных символов на их строчные эквиваленты.
Как и команда s///, команда y/// принимает один или несколько параметров, следующих за последней косой чертой команды. Ни одна из этих опций, скорее всего, не будет полезна для общих целей, но c и d могут иметь некоторые нишевые применения, и об этом ниже.
Опции y///
Как и команда s///, команда y/// может принимать несколько опций, и каждая опция по-своему изменяет поведение команды y///. Среди множества опций y/// можно выделить две, «c» и «d», как наиболее полезные.
Обе эти опции используются в связи с присущим y/// поведением, известным как сминание: если количество символов в списке замены меньше количества символов в списке поиска, последний символ в списке замены дублируется до тех пор, пока длина списков поиска и замены не сравняется. Например,
y/[A-Z]/x/
эквивалентно:
y/[A-Z]/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/
Оба выражения заменят любую прописную букву на строчную x. Однако первое выражение гораздо компактнее и легче читается.
Опция «c», предписывает y/// дополнить список символов в списке поиска и заменить любой символ, отсутствующий в списке. В сочетании со сглаживанием это можно использовать для замены запрещенных символов, отсутствующих в списке поиска, на один конкретный символ-заместитель. Напомню, что файловые системы *nix/Linux могут обрабатывать большинство непечатаемых символов в именах файлов. Например, если у вас есть файлы с непечатаемыми символами в именах, вы можете быстро очистить такие имена файлов, заменив все неалфавитные, нечисловые и прочие символы в именах файлов на символы точки (.), как в примере:
y/[A-Z][a-z][0-9]_-/./c
Другая опция «d», отключает сминание и удаляет любой символ в конце списка замены, не имеющий соответствующего символа в списке поиска. Таким образом,
y/.[A-Z]/.[a-d]/d
преобразует имя файла «DOC_1993.BAK» в «dc_.ba». Хотя этот пример надуман, он характерен для переключателя опций с ограниченной практической пользой.
Перемещение файлов между каталогами
Ещё одно возможное использование переименования — размещение каждой категории лог-файлов в своем каталоге. Как вы помните, у меня есть пять лог-файлов с именами daemon, syslog и messages. На практике, таких файлов в каталоге может оказаться сотни или даже тысячи. Следовательно, я хочу переместить каждый тип лог-файла в свой собственный каталог, например файл «syslog_13-10-2019_23:36:11» должен быть перемещен в каталог «syslog». В идеале я бы хотел, чтобы начальная часть имени лог-файла была удалена, поскольку имя содержащегося каталога должно чётко указывать на тип лог-файла. Ниже показано желаемое результирующее дерево каталогов.
ls -FNR
.:
daemon/ messages/ syslog/
./daemon:
09-03-2020_07:18:42
./messages:
02-04-2023_09:32:00 13-12-2021_13:43:27
./syslog:
13-10-2019_23:36:11 26-07-2022_18:56:03
И опять-таки, rename может перемещать файлы так же легко, как и переименовывать их. Более того, он может сделать и то, и другое в один приём. Очевидно, что в данном случае я хочу сделать и то, и другое одновременно, потому что я хочу переместить файл, а затем удалить первую часть его имени.
К сожалению, чтобы переместить файл в другой каталог, rename требует, чтобы каталог назначения уже существовал. Перед запуском rename вам придется предварительно создать все необходимые каталоги. Я использовал следующую однострочную команду оболочки для создания каталогов перед запуском rename:
find . -maxdepth 1 -type f -printf '%f\0' | grep -Eoz '^[^_]+' |xargs -0 mkdir
В этой команде перечислены все файлы, находящиеся непосредственно в текущем каталоге, а затем берется часть имени файла до первого подчеркивания (например, «messages») и создается новый каталог в текущем каталоге с именем, соответствующим первой части имени файла.
Теперь, чтобы переместить каждый файл журнала и затем удалить начальную часть имени каждого файла, я использую:
rename 's/^([^_]+)_/$1\//' *
Здесь следует отметить несколько моментов. Во-первых, я указал rename искать в самом начале имени файла строку любой длины, которая не содержит подчеркивания (^([^_]+) в поисковом выражении. Это позволяет использовать тот факт, что тип файла журнала отделяется от даты подчеркиванием. Затем я использую обратную ссылку, за которой следует косая черта в выражении замены, чтобы указать rename переместить файл в каталог, названный так, как было сопоставлено вышеупомянутое выражение в круглых скобках.
Обратите внимание, как я экранировал символ косой черты «\/», чтобы гарантировать, что rename не примет косую черту за конец выражения замены. Помните, что выражения поиска и замены, а также любые опции команды s/// разделяются символами косой черты. На самом деле я мог бы использовать практически любой символ для разделения частей команды s///, хотя использование косой черты является общепринятым. Я мог бы использовать знак «@» в приведенной выше команде rename или в любой из предыдущих команд s///. Следующая команда сработала бы так же хорошо:
rename 's@^([^_]+)_@$1/@' *
Благодаря использованию символа, отличного от косой черты, для разделения частей команды s///, мне больше не нужно экранировать косую черту в выражении замены, обозначающем часть пути к каталогу. На мой взгляд, это делает команду более удобной для чтения. Просто убедитесь, что выбранный вами символ не встречается ни в выражении поиска, ни в выражении замены, или же экранирован там, где он встречается.
Заключение
После того как вы поймете синтаксис команды rename, она станет эффективной и очень мощной утилитой для выполнения практически любых задач по массовому переименованию — от преобразования имен файлов в регистр заголовков до перемещения файлов в разные каталоги и замены номеров месяцев на их названия (например, 2015-02-17 на 2015-Feb-17). Все эти и другие задачи можно выполнить с помощью rename. Более того, несколько заданий можно объединить в одну команду для еще большей мощности и гибкости.
В этой статье мы рассмотрели ряд примеров, демонстрирующих основные возможности rename, но я лишь прошёлся по поверхности того, что можно сделать с помощью этой команды. Надеюсь, вы вдохновитесь на создание собственных команд переименования.