Reputation: 777
Using Powershell 7, this works perfectly:
dir foo* | Rename-Item -Path {$_.Name} -NewName{$_.Name -replace 'bar-(.*)', 'bar-$1'}
This fails:
dir foo* | Rename-Item {$_.Name} {$_.Name -replace 'bar-(.*)', 'bar-$1'}
With the following error:
Rename-Item: A positional parameter cannot be found that accepts argument '$_.Name'.
Based on the docs here -Path
is the first position of Rename-Item
. Also, the following command works: Rename-Item 'foo-one' 'foo-one-one'
. Why does the above failure happen?
Upvotes: 2
Views: 1302
Reputation: 61208
Although your example code does not actually rename any file (-replace 'bar-(.*)', 'bar-$1'
will result in the exact same name.), the main problem here is that you add {$_.Name}
directly after the Rename-Item
cmdlet, where this is not needed, since you are already piping fileInfo objects through the pipeline, so now {$_.Name}
is treated as if it were a parameter name.
Next, you forget to add the name for parameter -NewName
.
By iterating through a path like foo*
you do the same as -Path 'foo' -Recurse
and there's a gotcha in that..
Especially when iterating through many files and passing them on through the pipeline, there is a chance that files that have already been renamed get picked up by the Get-ChildItem
(alias dir
) again and then will be renamed twice.
To overcome that, put the part to gather the files between brackets, so collecting all the FileInfo objects is complete and then pipe that to the next cmdlet like so:
(Get-ChildItem -Path 'foo' -File -Recurse) |
Rename-Item -NewName {$_.Name -replace 'bar-(.*)', 'baz-$1'}
for clarity, this does change part of the name from bar
to baz
Upvotes: 2