Windows PowerShell Tip of the Week
Here’s a quick tip on working with Windows PowerShell. These are published every week for as long as we can come up with new tips. If you have a tip you’d like us to share or a question about how to do something, let us know.
Find more tips in the Windows PowerShell Tip of the Week archive.
Taking Things (Like File Paths) Literally
One of the great strengths of Windows PowerShell is its ability to deal with a little ambiguity. For one thing, PowerShell allows you to use wildcards pretty much any time and any place; for a few examples of this, see our previous tip on using the range operator in wildcard queries. For another, PowerShell doesn’t always require you to be painstakingly exact when you type in a command. For example, suppose you want to return a collection of all the files and folders found in C:\Scripts and its subfolders. If you want to, you can retrieve that data by using the following command, specifying the –recurse parameter:
Get-ChildItem C:\Scripts -recurse
Alternatively, you can retrieve that same information by using this command:
Get-ChildItem C:\Scripts -r
Notice that, in this latter case, we didn’t completely spell out –recurse; instead, we typed in just enough of the parameter name (-r) so that PowerShell could figure out which parameter we were talking about. Because Get-ChildItem has only one parameter that begins with the letter r, PowerShell can look at this “incomplete” command and go, “Oh: guess they wanted to use the –recurse parameter here.” Does PowerShell’s ability to deal with ambiguity come in handy? You better believe it does.
However, that doesn’t mean that there won’t be times when PowerShell gets “fooled.” For example, suppose we have a folder (C:\Test) that contains the following files:
File.txt File.txt File.txt File.txt File.txt
We need some information about the first file; logically enough, we try to get that information by using this command:
And what do we get back after we run this command? This:
And no, there’s no need to adjust your computer monitor. If you don’t see any output there’s a reason for that: there isn’t any output. The command Get-Item C:\Test\File.txt isn’t going to return any data whatsoever.
Is that because of a bug in Windows PowerShell? No; in fact, PowerShell is doing exactly what it was designed to do. If you read our previous tip on using the range operator (and you did read our previous tip on using the range operator, didn’t you?) then you know that PowerShell uses square brackets as a wildcard character: square brackets enable you to search for items that fall within a specified range. For example, do we need a list of all the files whose file names start with the letters a, b, c, d, e, or f? No problem; this command will take care of that for us:
That’s nice, but what does that have to do with our failed Get-Item command? Well, like we said, square brackets are wildcard characters in Windows PowerShell; by default, PowerShell is going to interpret those brackets as wildcards. Let’s take a look at our failed command again:
How is PowerShell parsing this path? It’s looking for a path that starts with C:\Test\File followed by one of the characters within the square brackets; because we have only the number 1 inside the brackets that means that PowerShell is looking solely for a file path that starts with C:\Test\File1, and then ends with .txt. In other words, thanks to the wildcard, PowerShell is looking for this file:
Note. To prove that, create a file named File1.txt in the Test folder, issue the command Get-Item C:\Test\File.txt, and see what you get back.
And thus our problem: PowerShell is doing its best to figure out what we meant here. That’s fine, except this time we don’t want it to figure out what we meant here; instead, we want PowerShell to take this command literally, to look for the path exactly as it was typed. But how are we supposed to do that? After all, it’s not as though the Get-Item cmdlet (and a number of other cmdlets) has a parameter named –literalPath.
Wait, check that: as it turns out, the Get-Item cmdlet (and a number of other cmdlets) does have a parameter named –literalPath. Do we need to access a file whose path includes a reserved character (like a square bracket)? That’s fine; just use this command:
Get-Item -literalPath C:\Test\File.txt
If you run that command then you should get back something similar to this:
Directory: Microsoft.PowerShell.Core\FileSystem::C:\Test Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 12/5/2005 5:39 PM 26335 File.txt
Well, sort of. The -literalPath parameter will definitely enable you to get at a single file whose name includes a reserved character. But suppose you’d like to get back a list of all the files whose name includes a square bracket. Off the top of your head, you might think a command like this will do the trick:
Get-ChildItem -literalPath C:\Test\File[*].txt
So will that command do the trick? Well, let’s see:
Get-ChildItem : Illegal characters in path. At line:1 char:14 + Get-ChildItem <<<< -literalPath C:\Test\File[*].txt Get-ChildItem : Cannot find path 'C:\Test\File[*].txt' because it does not exist. At line:1 char:14 + Get-ChildItem <<<< -literalPath C:\Test\File[*].txt
Uh-oh. So what’s the problem here? Well, remember, the –literalPath parameter tells PowerShell, “Use this path exactly as we typed it in; don’t interpret any of the characters as wildcard characters.” Because PowerShell will dutifully do whatever we ask of it, it’s looking for a single file named C:\Test\File[*].txt. That’s a problem, because Windows doesn’t allow asterisks (*) in file names. Consequently, we get an error message regarding illegal characters (don’t worry, they aren’t really illegal; you won’t get arrested for trying this script yourself) and then, just for good measure, get told that the file C:\Test\File[*].txt doesn’t exist. You can’t use wildcard characters in a path supplied to –literalPath; if you could, that would defeat the whole point of using -literalPath in the first place.
So does that mean that there’s no way to get back a list of all the files whose file name includes a square bracket? Of course not; you just have to know a little trick. We can’t provide much information as to why this command works; about all we can do is tell you that this is an artifact of the way PowerShell parses file paths and wildcard characters. But this crazy-looking command will return information about any file that includes a left square bracket character ([) in the file name:
Two things to note here. First, we enclosed the path name in single quote marks. That’s not mandatory, but it makes our command a bit easier on the eyes. (As you’ll see in a minute). Second, we needed to preface the square bracket with two back tick characters (``); this tells PowerShell that we want to treat the left square bracket as a regular character and not a wildcard character. Like we said, it looks crazy, but it works:
Directory: Microsoft.PowerShell.Core\FileSystem::C:\Test Mode LastWriteTime Length Name ---- ------------- ------ ---- -a--- 12/5/2005 5:39 PM 26335 file.txt -a--- 6/13/2008 8:26 AM 0 file.txt -a--- 6/10/2008 6:39 PM 346864 file.txt -a--- 12/6/2005 9:34 AM 13374 file.txt -a--- 6/13/2008 8:26 AM 0 file.txt
Again, enclosing the path in single quotes is optional. Alternatively, we could leave off the single quote marks and, instead, preface the square bracket character with four back ticks:
Believe it or not, that will work. But it’s nasty-looking, isn’t it?
Oh, and if you want to retrieve all the files that actually have a value enclosed in square brackets (as opposed to just a left square bracket character), then use this command
We’ll let you parse that one yourself.
Before we call it a day lets quickly discuss two other characters – the single quote mark and the comma – that can also wreak havoc when included in a file path. For example, suppose our Test folder includes these two files:
With the single quote mark, the –literalPath parameter won’t do you any good; for some reason, PowerShell won’t interpret that character literally. Instead, if you want to access the file File’6.txt you’ll need to use a single back tick character to escape that quote mark:
Or, even easier, just enclose the path in double quotes:
And what if you need a list of all the files that have a single quote mark in their file names? Again, it’s double quote marks to the rescue:
The same thing is true of file names that include a comma: surround the file path with double quote marks and everything should be fine:
Get-Item "C:\Test\File,7.txt" Get-ChildItem "C:\Test\*,*"
We’ll see you all next week.