3

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

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

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

然而,所有处理的文件都是并且71_永远不会增加,并且文件名的前缀相同?为什么?$count$count++


在此处输入图像描述

4

3 回答 3

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

这将起作用:

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

  • 在脚本中,只要$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:count

于 2019-07-01T22:16:02.247 回答
1

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

Get-ChildItem $directory | 
    Sort-Object -Property LastWriteTime |
    ForEach-Object {
        $NewName = "{0}_{1}" -f $count++, $_.Name
        Rename-Item $_ -NewName $NewName -WhatIf
    }
于 2019-07-01T22:50:27.793 回答
1

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

$directory = 'c:\temp'

gci $directory\* | sort LastWriteTime |
foreach { $count = 71 } { rename-item $_ -newname ("{0}_{1}" -f
$count++, $_.Name) -whatif } { 'done' }
于 2019-07-02T04:41:15.157 回答