move-item doesn't work in loop move-item doesn't work in loop powershell powershell

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