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

++ Operator on Variable Is Not Changing As Expected In ScriptBlock

我正在尝试通过在文件中放置一个基于递增计数器的前缀来重命名文件,例如:

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

gci $directory | sort -Property LastWriteTime | `
rename-item -newname {"{0}_{1}" -f $count++, $_.Name} -whatif

然而所有处理的文件都是 71_$count++ 中的 $count 从不递增并且文件名的前缀相同?为什么?


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

  • - such as the one you passed to Rename-Item -NewName - and script blocks in 运行 在 child 范围 .

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

    • 要了解有关范围和隐式 local-variable 创建的更多信息,请参阅

解决方法

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

这会起作用:

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

  • 在脚本中,只要在脚本的top-level作用域.[=32=中初始化了$count变量]

    • 也就是说,如果您将代码移动到带有 function-local $count 变量的 函数 中,它不再有效。

灵活的解决方案需要可靠的 relative 参考 parent scope:

有两种选择:

  • 概念清晰,但冗长且相对较慢,因为必须调用 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:countany 范围内初始化和递增 global 变量,但是考虑到即使在脚本执行结束后全局变量仍然存在,您还应该事先保存任何预先存在的 $global:count 值,然后再恢复它,这使得这种方法不切实际。

@mklement0 的回答是正确的,但我认为这比处理引用更容易理解:

Get-ChildItem $directory | 
    Sort-Object -Property LastWriteTime |
    ForEach-Object {
        $NewName = "{0}_{1}" -f $count++, $_.Name
        Rename-Item $_ -NewName $NewName -WhatIf
    }

哇,最近经常出现这种情况。这是我目前最喜欢的 foreach 多脚本块替代方案。带有通配符的 gci 稍后会给出 $_ 的完整路径。您不需要在竖线或运算符后使用反引号延续字符。

$directory = 'c:\temp'

gci $directory\* | sort LastWriteTime |
foreach { $count = 71 } { rename-item $_ -newname ("{0}_{1}" -f
$count++, $_.Name) -whatif } { 'done' }