$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
脚本块)和计算属性中的脚本块在子作用域中运行。
Where-Object
将此与传递给and的脚本块进行对比ForEach-Object
,后者直接在调用者的范围内运行。
尚不清楚这种行为差异是否是故意的。
因此,尝试修改调用者的变量会创建一个在每次迭代中超出范围的块局部变量,以便下一次迭代再次看到原始值:
顺便说一句:通过引入反映当前管道对象序列号的自动变量,提议的未来增强功能将消除手动维护序列号的需要:请参阅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 调用会引入开销,而且有点冗长,但是:
使用该[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
变量被引用。