I want to love Powershell. There is much that is cool about it, and it is certainly beats batch files or (gag) VBScript. I'm a beginner, and perhaps I'll eventually learn to accept its quirks, but I bump into annoyances frequently enough that Powershell and I are still just friends.
What frequently happens is that I get excited about a cool new concept or feature, but some detail of the execution mars it. I may post more later about a script I've been trying to write, but for now here is a simple example.
Users of bash and other Unix shells will be familiar with the $? variable. It stores the exit status of the last command executed. Generally a value of zero means "success", and anything else indicates an error of some sort:
$ ls modlist.txt
modlist.txt
$ echo $?
0
$ ls nosuchfile
ls: cannot access nosuchfile: No such file or directory
$ echo $?
2
In the first case, because the ls command succeeded, the value of $? is 0 . In the second, the value of $? is 2, indicating an error. This is not very helpful at the command line, but when writing a script you might use it to branch depending on whether a command succeeded or not (though there are more concise ways to accomplish that).
In Powershell, exit statuses aren't puny integers: They are full-fledged objects, of type ErrorRecord. And instead of automatically saving just the last one, there's a ring buffer array with the last 256 (by default) error objects! The most recent error is stored in the first element of the array (at index 0):
PS H:\> dir nosuchfile
Get-ChildItem : Cannot find path 'H:\nosuchfile' because it does not exist.
At line:1 char:4
+ dir <<<< nosuchfile
+ CategoryInfo : ObjectNotFound: (H:\nosuchfile:String) [Get-ChildItem], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
PS H:\> $error[0]
Get-ChildItem : Cannot find path 'H:\nosuchfile' because it does not exist.
At line:1 char:4
+ dir <<<< nosuchfile
+ CategoryInfo : ObjectNotFound: (H:\nosuchfile:String) [Get-ChildItem], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
PS H:\> $error[0].CategoryInfo
Category : ObjectNotFound
Activity : Get-ChildItem
Reason : ItemNotFoundException
TargetName : H:\nosuchfile
TargetType : String
PS H:\> $error[0].TargetObject
H:\nosuchfile
PS H:\> $error[0].Exception
Cannot find path 'H:\nosuchfile' because it does not exist.
PS H:\>
As you can see, I tried to dir a non-existent file, and this generated an ErrorRecord which was stored in $error[0]. I can go back and examine the properties of this object, like CategoryInfo, TargetObject, Exception, etc. Neato. So far, so good.
Before I continue, you need to know that PowerShell also supports tab-completion of property names (kind of like IntelliSense), another great feature. So I can reference a property name with the first few characters of the name, press the <TAB> key, and the PowerShell interpreter will finish it for me:
PS H:\> $name = "David"
PS H:\> $name.le # Press <TAB> here ...
PS H:\> $name.Length
5
PS H:\>
It even gives me the canonical capitalization. Well done, PowerShell. But having grown accustomed to this tab-completion, it's only natural that I would try to use it with $error[0]. Recall that $error[0].Exception is a perfectly legal expression: this is the value of the Exception property of the object stored in $error[0]. But if I type $error[0].Exc<TAB>, nothing happens. Even worse, if I then finish typing it by hand, I find that there is no Exception property anymore!
PS H:\> $error[0].Exc # Press <TAB> here ... no auto-completion :^(
PS H:\> $error[0].Exception # Fine, I'll type the extra six characters msyelf.
PS H:\> $error[0]
What happened? Everything's gone all crazy! Let's see what's in $error[0] now:
PS H:\> $error[0]
Unable to find type [0]: make sure that the assembly containing this type is loaded.
Now I'm really confused, because I can't tell whether the error message is simply the display representation of the ErrorRecord object stored in $error[0], or whether that error message is about the $error array itself. I'm pretty sure it's the former. Going back through the $error array, I eventually find my nosuchfile error at index 2:
PS H:\> $error[1]
Unable to find type [0]: make sure that the assembly containing this type is loaded.
At line:1 char:10
+ $_val=[0] <<<<
+ CategoryInfo : InvalidOperation: (0:String) [], RuntimeException
+ FullyQualifiedErrorId : TypeNotFound
PS H:\> $error[2]
Get-ChildItem : Cannot find path 'H:\nosuchfile' because it does not exist.
At line:1 char:4
+ dir <<<< nosuchfile
+ CategoryInfo : ObjectNotFound: (H:\nosuchfile:String) [Get-ChildItem], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
So not only doesn't tab-completion of property names work when indexing array variables, but it appears to silently generate an ErrorRecord object which is then stored in the $error array. Perhaps there's a very good reason why it has to be this way (if so, let me know), but it's quite annoying.
Long story short: don't try to tab-complete $error array properties. I recommend assigning $error[0] to a new variable (like $err), and then working with that:
PS H:\> dir blargh
Get-ChildItem : Cannot find path 'H:\blargh' because it does not exist.
At line:1 char:4
+ dir <<<< blargh
+ CategoryInfo : ObjectNotFound: (H:\blargh:String) [Get-ChildItem], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
PS H:\> $err = $error[0]
PS H:\> $err.Exception # Here tab-completion will work
Cannot find path 'H:\blargh' because it does not exist.
PS H:\>
Then you can use tab-completion to your heart's content.
Posted by cradle at August 19, 2010 12:52 PMOK. Thanks. Keep me posted.
Posted by: Anonymous at September 17, 2010 4:56 PM