变量上的++运算符未按ScriptBlock中的预期进行更改

Ωme*_*Man 3 powershell scope pipe scriptblock rename-item-cmdlet

我试图通过在文件中添加基于递增计数器的前缀来重命名文件,例如:

$directory = 'C:\Temp'
[int] $count=71; 

gci $directory | sort -Property LastWriteTime | `
rename-item -newname {"{0}_{1}" -f $count++, $_.Name} -whatif
Run Code Online (Sandbox Code Playgroud)

然而,所有的文件处理是71_$count$count++从不增量和文件名前缀相同?为什么?


在此处输入图片说明

mkl*_*nt0 5

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

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

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

    • 要了解有关范围和隐式局部变量创建的更多信息,请参见此答案

解决方法

一个实用的但可能有限制的解决方法是使用范围说明符$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
Run Code Online (Sandbox Code Playgroud)

这将起作用:

  • 在交互式会话中(在命令提示符下,在全局范围内)。

  • 在脚本中,只要$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
Run Code Online (Sandbox Code Playgroud)
  • 有点晦涩,但是更快更简洁([ref] $count).Value++
gci $directory | sort -Property LastWriteTime |
  rename-item -newname { '{0}_{1}' -f ([ref] $count).Value++, $_.Name } -whatif
Run Code Online (Sandbox Code Playgroud)

[ref] $count实际上与Get-Variable -Scope 1 count(假设$count在父作用域中设置了变量)相同


注意:从理论上讲,您可以在任何范围内$global:count用于初始化和递增全局变量,但是鉴于全局变量即使在脚本执行结束后仍然存在,您还应该事先保存任何先前存在的值,然后再进行恢复,这使得做法不切实际。$global:count