Начало: «
PowerShell: автоматизация переименования множества файлов, ч.1».
Работа с вложенными папками, рекурсия
Командлет «
Get-ChildItem» способен работать со вложенной структурой папок произвольной глубины вложения. Для этого у этого командлета есть параметры -Recurse и -Depth. С помощью параметра -Recurse командлету «Get-ChildItem» можно указать, чтобы он просматривал и вложенные папки. С помощью параметра -Depth можно ограничивать, на какую максимальную глубину будет опускаться при просмотре папок командлет «Get-ChildItem». Если ни один из этих параметров не указан, командлет «Get-ChildItem» просмотрит только текущую папку, без захода во вложенные папки.
В блок кода, написанный в
предыдущем посте, я добавлю командлету «Get-ChildItem» параметр -Recurse, чтобы выполнить запланированные переименования файлов и во вложенных папках на любой глубине вложения. Параметр -Depth мне не потребуется, так как мне не нужно ограничивать глубину просмотра папок командлетом «Get-ChildItem». Вот измененный код (я пометил дополнение синим цветом):
Get-ChildItem -File -Recurse |
Rename-Item -NewName {
$_.Name -replace '^(\d\d)-(\d\d)_(.*)', '$1-0$2_$3'
}
Этот код выполняет все требования, которые я излагал при постановке задачи в прошлом посте. Его уже можно использовать в работе. Однако, мне хотелось бы получать информацию о ходе переименования и итоговых результатах.
Вывод в консоль сообщений, информирующих о ходе работы скрипта
Чтобы можно было следить за ходом переименования файлов, я создал в имеющемся скриптовом блоке две локальные переменные: $old и $new, содержащих старое (до переименования) и новое (после переименования) имя файла соответственно. Если переименование произошло (значения переменных $old и $new отличаются друг от друга), то выводим в окно консоли строку следующего вида:
старое имя файла --> новое имя файла
В противном случае, если переименования не произошло (значения переменных $old и $new не отличаются друг от друга), то выводим в окно консоли строку другого вида:
новое имя файла
Вот как я реализовал это в коде:
Get-ChildItem -File -Recurse |
Rename-Item -NewName {
$old = $_.Name
$new = $old -replace '^(\d\d)-(\d\d)_(.*)', '$1-0$2_$3'
if ($old -ne $new) {
Write-Host "$old --> $new"
} else {
Write-Host "$new"
}
$new
}
Последнее утверждение $new в скриптовом блоке выполняет возврат нового названия файла из скриптового блока для передачи в параметр -NewName командлета «Rename-Item». Без этого утверждения параметр -NewName не получит нужного значения и скрипт при своей работе свою задачу не выполнит, выдаст ошибки.
Вывод в консоль сообщения о результатах работы скрипта
Мне хотелось, чтобы скрипт в конце своей работы отчитался, сколько файлов проверено всего и сколько из них переименовано. Для подсчета этих двух показателей я создал две глобальные (в рамках скрипта) переменные: $total и $renamed. Изменяем код (дополнения я пометил синим цветом):
$total = 0
$renamed = 0
Get-ChildItem -File -Recurse |
Rename-Item -NewName {
$old = $_.Name
$new = $old -replace '^(\d\d)-(\d\d)_(.*)', '$1-0$2_$3'
$script:total++
if ($old -ne $new) {
Write-Host "$old --> $new"
$script:renamed++
} else {
Write-Host "$new"
}
$new
}
"Проверено файлов всего: $total, из них переименовано: $renamed"
Изнутри
скриптового блока по умолчанию не получится изменить внешнюю для скриптового блока переменную (в нашем случае переменные $total и $renamed определены снаружи скриптового блока), так как у скриптового блока своя собственная область видимости (по-английски «scope»; подробнее про области видимости
читайте в документации), отдельная от области видимости скрипта.
Обычно в скриптовый блок значения передают через параметры скриптового блока, точно так же, как в случае с функциями. Но в нашем случае мне захотелось воспользоваться внешними переменными, поэтому я воспользовался для переменных $total и $renamed внутри скриптового блока одним из
модификаторов области видимости - script:. С помощью этого модификатора мне удалось из скриптового блока изменить значения внешних для этого скриптового блока переменных.
Файл для скрипта
Полученный скрипт я сохранил (добавив в него поясняющие комментарии) в файл «rename-files.ps1» в кодировке UTF-8 без метки BOM (см. в
моём репозитории на «Гитхабе»). Такой файл можно будет без проблем запустить из программы-оболочки «PowerShell» версии 7. Для программы-оболочки «Windows PowerShell» версии 5.1 при использовании кодировки UTF-8 метка BOM будет нужна обязательно. В качестве окончаний строк в коде я использую последовательность CRLF (это характерно для операционных систем «Windows»).
Порядок работы со скриптом, тестирование
Файл «rename-files.ps1» со скриптом я помещаю в корневую папку проекта и запускаю его из программы-оболочки «PowerShell» версии 7 (напомню, я работаю в операционной системе «Windows 10»). Пример запуска скрипта для проекта (папки) с названием «test»:
PS C:\Users\Илья\source\repos\test> .\rename-files
02-002_name2.cpp
02-01_name1.cpp --> 02-001_name1.cpp
02-03_name3.cpp --> 02-003_name3.cpp
rename-files.ps1
01-001_name1.cpp
01-003_name3.cpp
01-02_name2.cpp --> 01-002_name2.cpp
Проверено файлов всего: 7, из них переименовано: 3
После тестирования я проверил работу полученного скрипта на моём рабочем проекте, который описывал при постановке задачи в предыдущем посте (предварительно создав отдельную копию файлов проекта, чтобы ничего не потерять в случае ошибок в скрипте). Скрипт сработал так, как я и планировал: переименовал нужные файлы нужным мне образом.