1

如何使用以下代码在 powershell 中批量重命名文件:

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

其中PPPPPP是包含这些文件的父文件夹的名称。

预期输出:

PPPPPPP_001.jpg
PPPPPPP_002.jpg
PPPPPPP_003.jpg

文件位于 C:\USER\MAIN\BLABLABLA\PPPPPPP 文件夹中。

4

3 回答 3

1
  • $_.Directory.Name通过脚本块内部获取父目录的名称。

  • 用于Get-Variable获取对调用者$nr作用域中序号变量的引用,因此您可以直接(通过)修改其值,这比使用作用域修饰符更可取.Value$global:

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

-WhatIf 预览重命名操作;一旦您确信该命令将按预期执行,请将其删除。

  • 一种更简洁、更高效(但更模糊)的替代方法是将$nr变量转换为,[ref]以便您可以直接在调用者的范围内修改其值(通过.Value)。
$nr = 1
Get-ChildItem -Filter *.jpg | Rename-Item -Newname {
  '{0}_{1:d3}.jpg' -f $_.Directory.Name, ([ref] $nr).Value++
} -WhatIf

以下部分解释了这些技术。


可选阅读:在延迟绑定脚本块或计算属性中修改调用者的变量:

您不能只$nr++在脚本块中使用以直接增加序列号的原因是:

  • 延迟绑定脚本块(例如传递给 的Rename-Item -NewName脚本块)和计算属性中的脚本块在作用域中运行。

  • 因此,尝试修改调用者的变量会创建一个在每次迭代中超出范围的局部变量,以便下一次迭代再次看到原始值:

  • 顺便说一句:通过引入反映当前管道对象序列号的自动变量,提议的未来增强功能将消除手动维护序列号的需要:请参阅GitHub 问题 #13772 $PSIndex

以计算属性为例:

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

虽然这种技术很强大,但 cmdlet 调用会引入开销,而且有点冗长,但是:

  • 为简洁起见,您可以省略-Scope论证。

  • 或者,您可以通过以下方式提高效率:

    $nr = 1; $nrVar = Get-Variable nr
    1..2 | Select-Object { '#' + $nrVar.Value++ }
    

使用该[ref]类型提供了一个更简洁的替代方案,尽管解决方案有点晦涩

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变量被引用。

于 2018-11-20T13:23:44.753 回答
0

另一种略有不同的方式:

$nr=1;Get-ChildItem -Filter *.jpg |
Rename-Item -Newname {"$(split-path -leaf $_.Directory)_{0:d3}.jpg" -f
$global:nr++}
于 2019-06-30T22:44:55.407 回答
0

编辑:由于mklement0的提示而修改。
要获取父名称,.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".
于 2018-11-20T13:05:49.960 回答