我正在尝试通过在文件中添加基于递增计数器的前缀来重命名文件,例如:
$directory = 'C:\Temp'
[int] $count=71;
gci $directory | sort -Property LastWriteTime | `
rename-item -newname {"{0}_{1}" -f $count++, $_.Name} -whatif
然而,所有处理的文件都是 71_
和 $count++
中的 $count
从不递增且文件名前缀相同?为什么?
最佳答案
您不能在脚本 block 中直接使用 $count++
来直接增加序列号的原因是:
Delay-bind script blocks - 例如您传递给
Rename-Item -NewName
的那个 - 和 calculated properties 中的脚本 block 在子范围内运行。- 将此与传递给
Where-Object
和ForEach-Object
的脚本 block 进行对比,它们直接在调用者的范围内运行。
是unclear whether that difference in behavior is intentional .
- 将此与传递给
因此,尝试修改调用者的变量反而会创建一个 block -在每次迭代中都超出范围的局部变量,以便下一次迭代再次从调用者的范围中看到原始值。
- 要了解有关范围和隐式局部变量创建的更多信息,请参阅 this answer .
解决方法
一个实用但可能有限制的解决方法是使用范围说明符 $script:
- 即 $script:count
- 来引用调用者的 $count
变量:
$directory = 'C:\Temp'
[int] $count=71
gci $directory | sort -Property LastWriteTime |
rename-item -newname { '{0}_{1}' -f $script:count++, $_.Name } -whatif
这会起作用:
在交互式 session 中(在命令提示符下,在全局范围内)。
在脚本中,只要
$count
变量在脚本的顶级作用域中被初始化。- 也就是说,如果您将代码移动到带有局部函数
$count
变量的函数,它将不再有效。
- 也就是说,如果您将代码移动到带有局部函数
灵活的解决方案需要对父级范围的可靠相对引用:
有两种选择:
- 概念清晰,但冗长且相对较慢,因为必须调用 cmdlet:
(Get-Variable -Scope 1 count).Value++
gci $directory | sort -Property LastWriteTime |
rename-item -newname { '{0}_{1}' -f (Get-Variable -Scope 1 count).Value++, $_.Name } -whatif
- 有点晦涩,但更快更简洁:
([ref] $count).Value++
gci $directory | sort -Property LastWriteTime |
rename-item -newname { '{0}_{1}' -f ([ref] $count).Value++, $_.Name } -whatif
[ref] $count
实际上与 Get-Variable -Scope 1 count
相同(假设设置了一个 $count
变量在父范围内)
注意:理论上,您可以使用$global:count
在任何 范围内初始化和递增一个global 变量,但给定即使在脚本执行结束后全局变量仍然存在,您还应该事先保存任何预先存在的 $global:count
值,然后再恢复它,这使得这种方法不切实际。
关于powershell -++ 变量上的运算符未按 ScriptBlock 中的预期更改,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56843044/