Как массово переименовать файлы по маске в PowerShell
Невинный пост в группе ВК о сочетании клавиш проводника Ctrl + Shift + N внезапно зацепил приверженцев альтернативных файловых менеджеров. По ходу дела всплыла тема массового переименования файлов.
Евгений Генеральчик подметил, что в отличие от Total Commander, проводнику не по зубам переименование 20 тысяч файлов, например, добавление к имени файла текущей даты. Действительно, в классической серии пенальти я сам признал незаменимым сочетание Ctrl + M в TC, сфокусировавшись на функции поиска/замены.
Однако, отвечая Евгению в ВК, я заметил, что его задачу переименования решу в PowerShell и даже ванлайнером . Написал и подумал – надо все-таки решить, а то вдруг попросят пруфов :)
[+] Сегодня в программеПереименование файлов методом «поиск / замена»
Через несколько минут у меня получился такой вариант:
На самом деле в команде одна строка, а на две я ее разбил, чтобы она лучше читалась. Давайте разберем составляющие команды.
Выборка файловДля получения списка файлов я использую командлет Get-ChildItem. Он же gci, он же ls, он же Элла Кацнельбоген dir, а эта команда прекрасно знакома всем, кто написал хоть один батник .
- -Path $env:userprofile\Downloads – файлы ищутся по заданному пути, т.е. в папке Downloads в профиле пользователя
- -Filter "*.zip" – фильтруются только файлы с расширением ZIP. Впрочем, в выборку попадут также и файлы, содержащие в имени ".zip", но этим я пренебрегу. В принципе, можно не указывать параметр, а просто написать .zip.
- -Recurse – поиск ведется рекурсивно, т.е. во всех вложенных папках. Кстати, в PowerShell 5.0, т.е. в Windows 10, у gci появился новый параметр -Depth, задающий глубину рекурсии. Например, 0 – только текущая папка, 2 – на два уровня вглубь.
Выбранные файлы по конвейеру передаются дальше для переименования.
Переименование файловКомандлет Rename-Item тоже обладает хорошо известным псевдонимом ren. С помощью параметра -NewName задается новое имя, а в фигурных скобках стоит выражение, задающее условия переименования
- $_.Name – для каждого переданного по конвейеру файла $_ изменяется полное имя .Name
- -Replace "\.zip$", "-дата.zip" – я слегка упростил конструкцию, написав дату словом, чтобы был понятен смысл аргументов оператора -Replace: ‘что ищем’, ‘на что меняем’. Здесь тонкость в том, что у -Replace первый аргумент — регулярное выражение, а не просто поиск текста. В регулярных выражениях точка означает любой символ, поэтому использование ".zip" чревато нежелательным переименованием, например, файла winzip-setup.exe.
- "-$(Get-Date -Format ).zip" – подставляем мы все-таки текущую дату в формате 2015-09-04. Формат дат я уже разбирал, а здесь хочу обратить ваше внимание на другой фокус. Выражение (в данном случае – командлет) не получится вставить просто так в аргумент оператора, но его можно поместить между круглыми скобками такой конструкции $().
- -WhatIf – этот стандартный параметр в переводе означает «что будет, если». Когда он добавлен в команду, ее реального выполнения не происходит, а в консоль выводится результат того, что могло бы произойти без этого параметра. Другими словами, вы можете проверить команду у себя, не опасаясь, что все архивы переименуются :)
Переименование файлов путем добавления счетчика
По ходу обсуждения в ВК выяснилось, что пример с датой надуманный, а более реальной выглядит последовательная нумерация файлов. Действительно, в TC я часто использовал очень удобный счетчик [C] для переименования графических файлов.
В PowerShell его тоже можно реализовать. Допустим, надо добавить счетчик из трех цифр в конце имени всех файлов JPG в текущей папке. Команда может быть такой:
С выборкой все понятно, поэтому давайте разберем переименование.
- % — псевдоним командлета Foreach-Object, который перебирает по одному все переданные по конвейеру объекты. За ним следуют два блока скриптов в фигурных скобках < >, о которых чуть ниже.
- – первый блок, в котором переменная i получает значение 1. Это нужно для счетчика, который после переименования каждого объекта будет увеличиваться на единицу с помощью $i++.
- ren $_ — начало второго блока для переименования каждого полученного по конвейеру объекта.
- "$($_.Basename)-.png" – новое имя файла. Конструкцию $() я разбирал выше, но здесь я использую свойство Basename, т.е. имя файла без расширения (Name – имя файла с расширением).– счетчик, причем в данном случае он трехзначный, что определяется конструкцией из трех символов 00# после двоеточия. Эквивалентом будет , где число после D задает количество символов счетчика.
Существует три типа блоков:
- Begin (начало) — выполняется один раз в самом начале, до перебора элементов
- Process (обработка) — выполняется для каждого элемента
- End (конец) — выполняется в самом конце перебора
Командлет Foreach-Object обрабатывает блоки особым образом. Если ему указать только один блок, он воспринимает его как Process. Если два, то первый будет Begin, а второй — Process (наш случай). Если все три, то поведение стандартное: Begin → Process → End.
Дискуссия
Я не ставил перед собой задачу описать на языке PowerShell эквиваленты всех функций массового переименования Total Commander. Думаю, что это вполне возможно, а степень успеха зависит лишь от вашего уровня владения PowerShell, .NET и гуглояндексом. Вопрос лишь в том, насколько это целесообразно.
Графический интерфейс TC понятнее, нагляднее и не требует специальной подготовки. Однако бывают ситуации, когда вы не сможете воспользоваться любимым инструментом. Например, его нет в системе, а установить нельзя, поскольку ПК изолирован от внешней среды или это запрещено политикой организации. Если вы не ленитесь расширять свой кругозор и не упускаете случая поковырять PowerShell, вы с честью выйдете из такой ситуации.
Кстати, в ближайших записях я познакомлю вас с некоторыми новыми возможностями платформы PowerShell 5.0, включенной в Windows 10.
Расскажите в комментариях, какую задачу вы в последний раз решали:
- с помощью PowerShell или другого скриптового языка
- средства для массового переименования в любимом файловом менеджере
Я благодарю Василия Гусева за ценные дополнения, которые я добавил в статью после публикации.
Об авторе
Вадим - владелец этого блога, и почти все записи здесь вышли из-под его пера. Подробности о блоге и авторе здесь. Поддержать автора вы можете тут.
Вас также может заинтересовать:Я в Telegram
Подпишитесь на канал и читайте интересные записи чаще! Есть вопросы? Задайте их в чате.
комментариев 50знак «`» не обязательно ставить, т.к. PowerShell после | будет искать продолжение строки.
Паша, спасибо, я учту.
В последний раз решал задачу посылки любого sms сообщения через usb gsm «свисток». Причем скрипт работает как в оконном режиме(PowerGUI помог), так и в консольном.
Завидую (по хорошему) Вашей памяти, Вадим. Мне вот никак не удается весь этот «язык пляшущих человечков» запоминить. Это я относительно Вашей фразы про то, как PoSH может помочь в случае если Total поставить нельзя. Мне в этом случае поможет либо многочасовое чтение манов либо (если вдруг на таком ПК окажется инет) гугление.
Алекс, вы же ИТ-специалист? Тогда базовым английским владеете, а суть командлетов понятна из названия. Я их намного лучше понимаю, чем синтаксис команды FOR из CMD, который я так и не осилил, и без знания которого такие задачи не решаются.
Да, в этом случае Get-ChildItem не слишком очевиден, но это потому, что он не только к папкам применим, а еще и к реестру (gci hkcu:), хранилищу сертификатов и т.п. Но я в записи подчеркиваю псевдонимы dir, ren — не сложнее, чем в CMD :)
А нет ли способов уменьшить нагрузку на память, для уже решенных задач? Простых и быстрых. Записать эту команду в функцию и вызывать BulkRename *.jpg . или BulkRename *.jpg ., где «2» — количество знаков в нумерации, а «1» начальное значение счетчика.
Можно и функцию написать, было бы желание (у меня его нет :)
Кто-нибудь объяснит, зачем переименовывать 20000 файлов? Такое бывает вообще? Приведите примеры. А сколько файлов можно переименовать в проводнике? Хотя опять напрашивается вопрос — Зачем. Разве в Проводнике недостаточно инструментов для управления файлами?
Ок, пусть 20 файлов — как решать будете обе задачи в проводнике?
Да в легкую. Даже не заморачиваясь ни на чем создал 40 файлов https://cloud.mail.ru/public/3Zbq/uzYUwJjnz И переименовал и задал теги. А Тотал командер может работать с тегами в этом ракурсе?
Мда… Ладно, проехали.
Оно то переименовало, только что вы с такими похабными именами делать будете?
Живой пример, надо загрузить галерею фоток к статье на новостной сайт.
Заливать как DSCN0005.JPG и т.п. не вариант, так как редактор потом никогда не разберется какие фотки откуда. Принято правило именовать фотки как имя_статьи-00N.JPG, все латиницей и без пробелов. Как это сделать проводником?
Скриптом без проблем, хотя мы используем Bulk Rename.
А вот еще пример https://cloud.mail.ru/public/4bbb/vdWnPN2yr Вообще не понимаю, в чем здесь увидели проблему. В проводнике все удобно работает. Никакие тоталы и фары не нужны. Никакие скрипты не нужны. Никакие языки не нужны. Или задача состоит в том, кто изощренней будет изголяться? Пусть те, кто тыркается с тоталом, лучше поучится работать с Проводником. Либо у них действительно, прямая дорога назад в дос.
Ваши примеры того, что вы не читали статью, больше не нужны, спасибо.
Ладно, статью вы не поняли, давайте тогда про другое: а если надо будет в какой-то тег каждого файла записать его номер, который присутствует в имени этого же файла, вы это сможете сделать проводником для 640 файлов?
Последнее из того, что могу вспомнить — сортировка файлов, извлечённых по сигнатурам из образа диска с убитой файловой системой. Была написана программа (на C++), которая посекторно сканирует файл образа диска (дамп, полученный программой dd, которая есть и для Windows) и при обнаружении известной сигнатуры начинает выписывать новый файл. Имена полученных файлов в большинстве случаев имеют вид: «XXXX YYYYYYY.rar», где XXXX — порядковый номер файла, YYYYYY — номер сектора, с которого файл начинается на диске.
Для сортировки файлов были написаны адские батники, которые могли: — Устранять копии. В первой версии файлы сравнивались каждый с каждым при помощи утилиты fc /b (fc умеет устанавливать errorlevel), но этот алгоритм имеет квадратичную сложность (время O(N²)), поэтому для больших наборов он уже не подходил. Стал перемещать файлы в каталог с именем из хеш-кода (вычисленного утилитой fciv.exe). Затем другой скрипт обходил подпапки и сохранял только самый первый файл из каждой из них (остальные удалял). — Добавлять примерную дату создания к имени каждого архива. Архив извлекался во временную папку с восстановлением точного времени всех временных штампов (winrar x -ts arc.rar temp\), циклом for /r перебирались все файлы и выбиралась самая поздняя дата модификации. — Сортировать архивы по содержимому. Утилитой rar.exe извлекался листинг каждого архива, команда find определяла наличие ключевого слова (она тоже устанавливает errorlevel в зависимости от результата), при успешном обнаружении архив перемещался в соответствующую подпапку.
О существовании средства массового переименования файлов в Far Manager’е я ничего не знаю, возможно есть соответствующие плагины, искать не пробовал.
В некоторых простых случаях для массового переименования/копирования файлов в Far я сначала создавал список файлов в новом текстовом файле: dir /b *.abc > rename.bat Затем правил rename.bat встроенным редактором, как правило, записывая макрокоманду. Потом нажимал горячую клавишу макрокоманды (как правило, Ctrl+Alt+↓) для каждой строчки в файле. Либо просто записывал макрокоманду, которая в командной строке Far’а формирует команду переименования файла под курсором, тут же её выполняет (нажимает Enter) и переходит к следующему файлу (нажимает ↓). И опять давил на горячую клавишу десяток-другой раз.
Возвращаясь к cmd.exe. Мне кажется, я слишком хорошо знаю обычную командную строку, чтобы заставить себя начать изучать PowerShell. Нумерация файлов в текущем каталоге:
Добавление текущей даты будет отличаться использованием %DATE% вместо %SUF:
-3% в процедуре RENAME. Недостаток по сравнению с однострочником PowerShell — нужно создавать файл. И я тоже не помню наизусть все ключи команд set и for, но их всегда можно посмотреть командами set /? и for /?.