Автоматическое пакетное переименование файлов в соответствии с формулой, включающей имя родительской папки, с помощью Powershell

Как я могу пакетно переименовать файлы в powershell, используя следующий код:

$nr=1;Get-ChildItem -Filter *.jpg |
  Rename-Item -Newname {"PPPPPPP_{0:d3}.jpg" -f $global:nr++}

где PPPPPPP - это имя родительской папки, содержащей эти файлы.

Ожидаемый результат:

PPPPPPP_001.jpg
PPPPPPP_002.jpg
PPPPPPP_003.jpg

Файлы находятся в папке C:\USER\MAIN\BLABLABLA\PPPPPPP.

2 ответа

  • Получить имя родительского каталога через $_.Directory.Name внутри блока скрипта.

  • Брось $nr переменная к [ref] так что вы можете изменить его значение непосредственно в области вызова (через .Value), что предпочтительнее использования модификатора области видимости $global: - увидеть ниже.

$nr = 1
Get-ChildItem -Filter *.jpg | Rename-Item -Newname {
  '{0}_{1:d3}.jpg' -f $_.Directory.Name, ([ref] $nr).Value++
} -WhatIf

-WhatIf предварительный просмотр операции переименования; удалите его, как только вы будете уверены, что команда будет работать так, как задумано.


Необязательное чтение: изменение переменных вызывающей стороны в блоке сценария с задержкой привязки или вычисляемом свойстве:

Причина, по которой вы не могли просто использовать $nr++ в вашем блоке сценария для того, чтобы увеличить порядковый номер непосредственно:

  • Блоки сценариев с задержкой привязки (например, переданные в Rename-Item -NewName) и блоки скриптов в вычисляемых свойствах выполняются в дочерней области.

  • Следовательно, попытка изменить переменные вызывающей стороны вместо этого создает блочную переменную-local, которая выходит из области действия на каждой итерации, так что следующая итерация снова видит исходное значение:

Используя вычисляемое свойство в качестве примера:

PS> $nr = 1; 1..2 | Select-Object { '#' + $nr++ }

 '#' + $nr++ 
-------------
#1
#1   # !! the *caller's* $nr was NOT incremented 

Хотя вы можете использовать модификатор области видимости, такой как $global: или же $script: чтобы явно ссылаться на переменную в родительской области видимости, это абсолютные области видимости, которые могут работать не так, как предполагалось: например: если вы переместите свой код в сценарий, $global:nr больше не относится к переменной, созданной с $nr = 1,

Кратко: создания глобальных переменных, как правило, следует избегать, учитывая, что они остаются в текущем сеансе даже после выхода из скрипта.

Надежный подход заключается в использовании Get-Variable -Scope 1 вызов надежно ссылается на непосредственную родительскую область:

PS> $nr = 1; 1..2 | Select-Object { '#' + (Get-Variable -Scope 1 nr).Value++ }

 '#' + (Get-Variable -Scope 1 nr).Value++ 
------------------------------------------
#1
#2  # OK - $nr in the caller's scope was incremented

Хотя этот метод является надежным, он также является громоздким и многословным.

Но вы можете улучшить эффективность следующим образом:
$nr = 1; $nrVar = Get-Variable nr; 1..2 | Select-Object { '#' + $nrVar.Value++ }

С использованием [ref] type предлагает более лаконичную альтернативу, хотя решение немного неясно:

PS> $nr = 1; 1..2 | Select-Object { '#' + ([ref] $nr).Value++ }

 '#' + ([ref] $nr).Value++ 
---------------------------
#1
#2  # OK - $nr in the caller's scope was incremented

Приведение переменной к [ref] возвращает объект, чей .Value свойство может получить доступ - и изменить - значение этой переменной. Обратите внимание, что с $nr не назначается в этот момент, это действительно вызывающий $nr переменная, на которую ссылаются.

РЕДАКТИРОВАТЬ: изменение из-за подсказки mklement0 s.
Чтобы получить имя родителя, используйте .Directory.Name в качестве другого параметра для оператора формата

$nr=1
Get-ChildItem *.jpg -file|
  Rename-Item -Newname {"{0}_{1:d3}.jpg" -f $_.Directory.Name,([ref]$nr).Value++} -whatIf

Если вывод выглядит нормально, удалите -WhatIf

Это будет работать только тогда, когда нет пересекающихся диапазонов, выполняющих переименование, в этом случае вам, вероятно, следует использовать временное расширение.

Пример вывода на мой немецкий язык Windows

WhatIf: Ausführen des Vorgangs "Datei umbenennen" für das Ziel "Element: A:\test\150.jpg Ziel: A:\test\test_007.jpg".
WhatIf: Ausführen des Vorgangs "Datei umbenennen" für das Ziel "Element: A:\test\151.jpg Ziel: A:\test\test_008.jpg".
WhatIf: Ausführen des Vorgangs "Datei umbenennen" für das Ziel "Element: A:\test\152.jpg Ziel: A:\test\test_009.jpg".
WhatIf: Ausführen des Vorgangs "Datei umbenennen" für das Ziel "Element: A:\test\153.jpg Ziel: A:\test\test_010.jpg".
WhatIf: Ausführen des Vorgangs "Datei umbenennen" für das Ziel "Element: A:\test\154.jpg Ziel: A:\test\test_011.jpg".
WhatIf: Ausführen des Vorgangs "Datei umbenennen" für das Ziel "Element: A:\test\155.jpg Ziel: A:\test\test_012.jpg".

Другой, немного другой способ:

$nr=1;Get-ChildItem -Filter *.jpg |
Rename-Item -Newname {"$(split-path -leaf $_.Directory)_{0:d3}.jpg" -f
$global:nr++}
Другие вопросы по тегам