move-item doesn't work in loop
Update: Based on the comment below, I want to clarify this: The special characters in the file names require you to use -LiteralPath parameter. -Path cannot handle those characters. Outside a loop, -Path works since you are escapting the special characters using `. This isn't possible when walking through a collection.
In a loop, you need to use -LiteralPath parameter instead of -Path.
-LiteralPath <string[]> Specifies the path to the current location of the items. Unlike Path, the value of LiteralPath is used exactly as it is typed. **No characters are interpreted as wildcards. If the path includes escape characters, enclose it in single quotation marks.** Single quotation marks tell Windows PowerShell not to interpret any characters as escape sequences.
SO, this will be:
GCI -Recurse *.txt | % { Move-Item -LiteralPath $_.FullName -Destination "SomenewName" }
If you use the pipeline binding feature of PowerShell, you can make this much simpler and eliminate the need for the explicit Foreach-Object
e.g.:
ls *.gif | Move-Item -Destination {$_ -replace '\[|\]',''} -WhatIf
This works because the LiteralPath
parameter is set up to bind ByPropertyName
. However you may wonder, where does it get a property by the name of "LiteralPath" from on the output of Get-ChildItem
(alias ls). Well it doesn't find that property name, however the LiteralPath
parameter has an alias of PSPath
defined which does exist on each object output by Get-ChildItem
. That's how it binds to the LiteralPath
paramter. The other speed tip here is that because the Destination
parameter is also pipeline bound (ByPropertyName), you can use a scriptblock
to provide the value. And inside that scriptblock you have access to the pipeline object.
Inside the scriptblock, this uses the -replace
operator to come up with the new name based on the original full name. While I could have used $_.FullName
or even $_.Name
in this case (assuming you want to essentially rename the files within the same dir), I use just $_
. Since -replace
is a string operator, it will coerce $_
to a string before using it. You can see what this would be by executing:
ls *.gif | Foreach {"$_"}
Which is the full path in this case but you have to be careful because you don't always get the full path e.g.:
ls | Foreach {"$_"}
displays just the filename. In your examples (rename to same dir) this doesn't matter but in other cases it does. It is probably a good practice just to be explicit and use $_.Name
or $_.FullName
in a script but when hacking this stuff out at the console, I tend to use just $_
. The saying: it's a sharp stick, don't poke your eye out applies here. :-)
You can find the "official" informations about the role of "[
" in Path strings on this Microsoft article.
Or look in google for : Windows PowerShell Tip of the Week : "Taking Things (Like File Paths) Literally".
The only tip wich was not clear for me is that Rename-Item
does not support LiteralPath
, and that we can use Move-Item
to rename files or directories.
JP