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? Why?
The reason you cannot just use $count++
in your script block in order to increment the sequence number directly is:
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.
Where-Object
and ForEach-Object
, which run directly in the caller's scope.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.
A pragmatic, but potentially limiting workaround is to use scope specifier $script:
- i.e., $script:count
- to refer to the caller's $count
variable:
$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
variable, it would no longer work.A flexible solution requires a reliable relative reference to the parent scope:
There are two choices:
(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
is effectively the same as Get-Variable -Scope 1 count
(assuming that a $count
variable was set in the parent scope)
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.