Powershell Bug in copy/move/rename when filename contains [square bracket characters]?

24,304

Solution 1

Unless you use -LiteralPath, square brackets cause real problems with character escaping. The problem comes from PowerShell de-escaping the strings multiple times internally and using special characters for pattern matching. Getting PowerShell to correctly recognize a literal square bracket in a path string is complex.

If you're using single-quote strings, a bracket needs two backticks to escape it. If you're using double-quote strings, a bracket needs four backticks to escape it.

If you're looking for a file named MyFile[1].txt, you need to use either:

'MyFile``[1``].txt'

or:

"MyFile````[1````].txt"

Yes, it's a pain. To see why it does this, you have to know what's going on. It's easy to do that by working backwards.

Let's say you want to get a file literally named [ab].txt.

The wildcard pattern matching that Get-ChildItem does means that if it gets [ab].txt as the path, then it will look for files named a.txt and b.txt. So, if we want to match a literal [ab].txt, we have to escape our brackets with the escape character: the backtick. That gives us this as the actual string of characters we want Get-ChildItem to use for the filespec:

`[ab`].txt

However, we have to pass this filespec as a string. That means Get-ChildItem will escape those backticks, but that's not what we want! We want literal backticks. So, we escape the backticks with backticks in our string to make sure Get-ChildItem uses the right filespec:

'``[ab``].txt'

If we want to use double-quoted strings, then we have to escape each backtick again, as the double-quoted string will de-escape the string. And that's how you end up with this:

"````[ab````].txt"

This is why so many PowerShell functions that take filespecs have the -LiteralPath option.

Solution 2

RenameItem doesn't have a -LiteralPath for some stupid reason.*

Move-Item -LiteralPath $i -destination $name

* Excuses from a co-designer of the language.

Solution 3

This finally achieved the desired result:

foreach ($i in get-childitem $folderpath) {
  if ($i.mode.substring(0,1) -ne “d”) {
    $name = $i.name.replace("[","_")
    $name = $name.replace("]","_")

   Write-Host $name -foregroundcolor “green”    
   [System.IO.File]::Move($folderPath+"\"+$i, $folderPath+"\"+$name) 
  }
}
Share:
24,304

Related videos on Youtube

TTrewitt
Author by

TTrewitt

Updated on September 17, 2022

Comments

  • TTrewitt
    TTrewitt almost 2 years

    Please pardon a noob question, but I've been completely baffled by this one.

    I have a "depositor" directory where user-submitted files arrive, and have no control over the incoming file names.

    I created a parser in PS which quite successfully moves files (based on file name content) to an appropriate destination.

    This works fine EXCEPT when a filename contains either "[" or "]".

    Here is the "rename" pre-processor, which fails to actually rename a file containing either of the pesky bracket characters:

     cd $folderpath
     foreach ($i in get-childitem $folderpath) {   
         if ($i.mode.substring(0,1) -ne “d”) {
            $name = $i.name.replace("[","_")
            $name = $name.replace("]","_")
            Write-Host $i -foregroundcolor “blue”       
            Write-Host $name -foregroundcolor “green”  
    
            Rename-Item $i $name 
    
         }
     }
    

    This also fails for ren, copy, move and their cmdlet equivalents

    Any insight you might be able to provide would be most welcome.

    Thanks in advance . . .

  • Dennis Williamson
    Dennis Williamson over 13 years
    @TTrewitt: What version of PS?
  • Philip Kendall
    Philip Kendall almost 11 years
    The link in this answer has gone 404. Anyone know of a replacement?
  • Dennis Williamson
    Dennis Williamson almost 11 years
    @PhilipKendall: I fixed the link.
  • AllGamer
    AllGamer over 3 years
    -LiteralPath option does not work in Powershell version1 (Windows default version), if you are unable to upgrade to a newer Powershell due to workplace restrictions (Admin rights), this is by far the best solution to deal with the square brackets problem.
  • velkoon
    velkoon about 3 years
    This answer introduced me to the Literal-Path parameter I needed to use for Set-ACL -LiteralPath that was interpreting [] in the filename as wildcards -- Thanks (Link doesn't work anymore sadly--even on archive.org :( )