Инструмент командной строки 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. Параметры переименования
Опция | Значение |
---|---|
-n, —nono | Не переименовывает и не перемещает никакие файлы. Этот параметр наиболее полезен в сочетании с параметром -v, чтобы показать, что будет сделано без фактического переименования файлов. |
-v, —verbose | Выводит имя каждого файла, как до применения выражения, так и после. Это полезно для проверки эффектов выражения rename, особенно в сочетании с опцией -n. |
-f, —force | Приступает к переименованию файлов, даже тех, которые после переименования будут иметь имена, конфликтующие с существующими файлами. Обычно rename не переименовывает файл, если файл с таким именем уже существует. При использовании rename переименованный файл перезапишет все существующие файлы с тем же именем. Используйте с осторожностью. |
—path, —fullpath | Оперирует полным именем пути к файлу, а не только самим именем файла. Например, замена всех экземпляров слова JPG на JPEG в файле по адресу Pictures/JPGs/1.JPG не только переименует файл в 1.JPEG, но и переместит его в Pictures/JPEGs/1.JPEG. Это поведение rename по умолчанию, поэтому вам редко придется указывать эту опцию явно. |
-d, —filename, —nopath, —nofullpath | Оперирует только именем самого файла, а не полным именем пути к нему. Замена всех экземпляров слова JPG на JPEG в файле Pictures/JPGs/1.JPG приведет к переименованию файла в Pictures/JPGs/1.JPEG. |
-u, —unicode | Обычно rename ожидает, что имена файлов будут представлять собой обычный ASCII-текст. Эта опция задает формат Unicode. Дополнительный параметр задает точную кодировку символов для имен файлов. |
Простой пример
Для первого примера я сохранил несколько 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. Метасимволы регулярных выражений
Опция | Значение |
---|---|
\ (обратный слэш) | Эскейпирует символ, следующий сразу за обратной косой чертой, так что следующий за ней символ интерпретируется буквально, а не как метасимвол. Используйте два последовательных обратных слеша (\) для соответствия одному буквальному символу обратного слеша. |
. (точка) | Сопоставляет любой одиночный символ. |
[and ] (квадратные скобки): | Совпадает с любым из символов, заключенных в квадратные скобки. Например, [Ahk7~] соответствует A, h, k, 7 или ~, но не другим символам и не комбинации из двух или более символов. Также поддерживаются диапазоны символов; например, [A-Z] соответствует любой отдельной прописной букве, а [A-Za-z0-9] — любой отдельной цифровой цифре или прописной или строчной букве. Если за открытой квадратной скобкой следует символ каретки (^), то соответствие инвертируется, и выражение в квадратных скобках будет соответствовать любому символу, не присутствующему в квадратных скобках; так, [^A-Z_] соответствует k, 6 и #, но не K, Z или символу подчеркивания (_). |
( and ) (в скобках) | Объединяет части регулярного выражения, которые обычно считаются отдельными, а также разделяет части, которые в противном случае считались бы одним компонентом. Например, (b |
? (знак вопроса) | Помечает предыдущий символ как необязательный (т.е. символ может либо не встречаться, либо встречаться ровно один раз). Например, z? соответствует либо z, либо пустой строке, но сам по себе не будет соответствовать zz или zzzzz. |
* (звездочка) | Приводит к совпадению с предыдущим символом в строке поиска, независимо от того, сколько или сколько раз он встречается подряд, даже если он не встречается вообще. Например, H* будет соответствовать H, HH, HHH, HHHH, HHHHHHHHHH или даже вообще ничего. |
+ (знак плюс) | Как и звездочка, приводит к совпадению с предыдущим символом в строке поиска, независимо от того, сколько или мало раз он встречается в строке, если он встречается хотя бы один раз. Например, H+ будет соответствовать H, HH, HHH, HHHH, HHHHHHHH, но не пустой строке. |
{ и } (фигурные скобки) | Приводит к совпадению с предыдущим символом, если он встречается несколько раз, причем это число находится между верхним и нижним диапазоном, указанным между скобками. Например, k{2,6} соответствует от двух до шести букв k подряд, но не семи и более, не одной k и не пустой строке. k{,6} эквивалентен k{1,6}, а k{3,} соответствует трем и более буквам k подряд. |
| (вертикальная черта) | Сопоставляет любое из двух (или, возможно, более) подвыражений. Например, cat|walrus соответствует либо кошке, либо моржу, (cat|walrus)walk соответствует либо catwalk, либо walruswalk, а cat|lion|weasel соответствует любому из слов cat, lion или weasel. |
^ (карет) | Сопоставляет начало строки. Сам по себе этот символ не совпадает ни с каким реальным символом; он просто указывает, что следующий символ в строке поиска должен находиться в самом начале строки. Как и ожидалось, каретка обычно должна быть первым символом в строке поиска. |
$ (символ доллара) | Сопоставляет конец строки. Как и знак каре, сам по себе он не совпадает ни с одним реальным символом и только сообщает rename считать предыдущий символ совпадением тогда и только тогда, когда предыдущий символ является последним символом в строке. Знак доллара имеет другое значение, если за ним следует цифра и/или если он появляется в выражении замены вместо выражения поиска (см. запись ниже). |
от $1 до $9 | Ссылается на определенную часть поискового выражения, заключенную в круглые скобки. $1 ссылается на то, чему соответствовало подвыражение, заключенное в первую пару скобок в поисковом выражении, $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, но я лишь прошёлся по поверхности того, что можно сделать с помощью этой команды. Надеюсь, вы вдохновитесь на создание собственных команд переименования.
Что вы думаете?
Показать комментарии / Оставить комментарий