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 . . .
Best Answer
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:or:
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 nameda.txt
andb.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 wantGet-ChildItem
to use for the filespec: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 sureGet-ChildItem
uses the right filespec: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:
This is why so many PowerShell functions that take filespecs have the
-LiteralPath
option.