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

时间:2019-07-01 22:08:36

标签: powershell scope pipe scriptblock rename-item-cmdlet

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

rosa = resource_path("rosa.png")

然而,所有处理过的文件都是$directory = 'C:\Temp' [int] $count=71; gci $directory | sort -Property LastWriteTime | ` rename-item -newname {"{0}_{1}" -f $count++, $_.Name} -whatif 中的71_$count永不递增,并且文件名是否前缀相同?为什么?


enter image description here

3 个答案:

答案 0 :(得分:5)

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

  • Delay-bind script blocks-例如您传递给Rename-Item -NewName的脚本-calculated properties中的脚本块在 child 范围内运行< / strong>。

  • 因此,试图修改调用方的变量,而是创建了一个 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

这将起作用:

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

  • 在脚本中,只要在脚本的顶级范围内初始化了$count变量。

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

灵活的解决方案需要可靠的 relative 引用 parent 范围

有两种选择:

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

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

哇,最近有很多事情发生。这是我当前最喜欢的foreach多脚本块替代方案。带有通配符的gci稍后提供了$ _的完整路径。您不需要在管道或运算符之后使用反引号连续字符。

$directory = 'c:\temp'

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