简体   繁体   English

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

[英]++ Operator on Variable Is Not Changing As Expected In ScriptBlock

I am trying to rename files by putting a prefix based on an incrementing counter in the files such as: 我试图通过在文件中添加基于递增计数器的前缀来重命名文件,例如:

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

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

Yet all the files processed are 71_ and $count in $count++ never increments and the filenames are prefixed the same? 但是所有处理过的文件都是$count++中的71_$count永不递增,并且文件名是否前缀相同? Why? 为什么?


在此处输入图片说明

The reason you cannot just use $count++ in your script block in order to increment the sequence number directly is: 您不能仅在脚本块中使用$count++来直接增加序列号的原因是:

  • Delay-bind script blocks - such as the one you passed to Rename-Item -NewName - and script blocks in calculated properties run in a child scope . 延迟绑定脚本块 (例如您传递给Rename-Item -NewName脚本块)和计算所得属性中的脚本块在作用域中运行。

  • Therefore, attempting to modify the caller's variables instead creates a block -local variable that goes out of scope in every iteration , so that the next iteration again sees the original value from the caller's scope. 因此, 尝试修改调用者的变量会创建一个局部变量,该变量在每次迭代中都超出范围 ,以便下一次迭代再次从调用者的范围中看到原始值。

    • To learn more about scopes and implicit local-variable creation, see this answer . 要了解有关范围和隐式局部变量创建的更多信息,请参见此答案

Workarounds 解决方法

A pragmatic, but potentially limiting workaround is to use scope specifier $script: - ie, $script:count - to refer to the caller's $count 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

This will work: 这将起作用:

  • in an interactive session (at the command prompt, in the global scope). 在交互式会话中(在命令提示符下,在全局范围内)。

  • in a script, as long as the $count variable was initialized in the script's top-level scope . 在脚本中,只要在脚本的顶级范围内初始化$count变量即可。

    • That is, if you moved your code into a function with a function-local $count variable, it would no longer work. 也就是说,如果你有一个函数本地移动你的代码到一个函数 $count变量,它不再工作。

A flexible solution requires a reliable relative reference to the parent scope : 灵活的解决方案需要对范围的可靠的相对引用

There are two choices: 有两种选择:

  • conceptually clear, but verbose and comparatively slow , due to having to call a cmdlet: (Get-Variable -Scope 1 count).Value++ 由于必须调用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
  • somewhat obscure, but faster and more concise : ([ref] $count).Value++ 有点晦涩,但更快,更简洁([ref] $count).Value++
gci $directory | sort -Property LastWriteTime |
  rename-item -newname { '{0}_{1}' -f ([ref] $count).Value++, $_.Name } -whatif

[ref] $count is effectively the same as Get-Variable -Scope 1 count (assuming that a $count variable was set in the parent scope) [ref] $count实际上与Get-Variable -Scope 1 count (假设在父范围中设置了$count变量)


Note: In theory, you could use $global:count to both initialize and increment a global variable in any scope, but given that global variables linger even after script execution ends, you should then also save any preexisting $global:count value beforehand, and restore it afterwards, which makes this approach impractical. 注意:从理论上讲,您可以使用$global:count任何范围内初始化和递增全局变量,但是鉴于全局变量即使在脚本执行结束后仍然存在,您还应该事先保存任何先前存在的$global:count值,并随后将其还原,这使该方法不切实际。

@mklement0's answer is correct, but I think this is much easier to understand than dealing with references: @ mklement0的答案是正确的,但是我认为这比处理引用更容易理解:

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

Wow, this is coming up a lot lately. 哇,最近出现了很多。 Here's my current favorite foreach multi scriptblock alternative. 这是我当前最喜欢的foreach多脚本块替代方案。 gci with a wildcard gives a full path to $_ later. 带通配符的gci稍后提供了$ _的完整路径。 You don't need the backtick continuation character after a pipe or an operator. 在管道或运算符之后,您不需要反引号连续字符。

$directory = 'c:\temp'

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM