PowerShell Troubleshooting Guide
上QQ阅读APP看书,第一时间看更新

Function output

The example function included in Chapter 1, PowerShell Primer, used string substitution and returned the single result of that operation. As a reminder, here it is again:

function Get-PowerShellVersionMessage{
param($name)
    $PowerShellVersion=$PSVersionTable.PSVersion
    return "We're using $PowerShellVersion, $name!"
}

This pattern (that is, performing a calculation and returning the result) is common to procedural programming languages such as C#, Java, and Visual Basic. In PowerShell, however, functions are more complicated than the usage in this scenario in several ways.

In statically-typed languages, the type of a function (that is, the type of a value returned by the function) is either declared as part of the function definition or inferred by the compiler. In PowerShell, the only consideration of a type associated with the output of a function is in the help provided for the function. This output type can be used by a PowerShell host to guide IntelliSense in the environment, but does not place any restrictions on the function in any way. That is, there is no constraint on what types of objects a function outputs. For example, the help for Get-Service indicates that it outputs System.Service.ServiceController objects, as shown in the following screenshot:

Because of this, when you use Get-Service in a pipeline, the environment can tell what properties are relevant, as shown in the following screenshot:

Tip

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

As a specific example of the varying output types of a function in PowerShell, consider the Get-ChildItem cmdlet. A similar command in the .NET framework would be the GetFiles static method of the System.IO.Directory class, which returns an array of strings corresponding to the files in a directory. Get-ChildItem, on the other hand, generally outputs a list of FileInfo objects, but depending on the particular parameters used it might output nothing, a list of FileInfo objects, or a single FileInfo object. Consider the following code to see an example of this in action:

$files = Get-ChildItem -path *.exe

foreach ($file in $files){
   # do something interesting
}

The code follows a common pattern (that is, get a list of objects and use foreach to iterate through the list) and the intent of the code is plain. A problem arises in trying to interpret the foreach loop over a value that is not a list. If there are no .exe files in the current directory, $files will be empty. By this I mean that $files won't be an empty list; it will simply be $null. What about the situation where there is exactly one .exe file? Does it make sense to loop through a single file? Again, this is not a list with one file in it, it is a single FileInfo object.

In PowerShell Versions 1.0 and 2.0, the answer was to be more careful when storing lists in variables. By adding a type of [array] to $files, we have instructed the engine to make sure that what is stored in the variable is indeed an array. In this case, the zero and single object cases result in an empty list and a list containing one object. The loop now makes sense:

[array]$files = Get-ChildItem -path *.exe

foreach ($file in $files){
   # do something interesting
} 

PowerShell Version 3.0 has a different approach to solving this problem. In this version, each non-null, non-collection object is given a Count property with a value of 1 and indexer that shows that the first item in the collection (at index 0) is the object itself. The built-in $null value has a Count property of 0. However, in the case of a null object, the indexer isn't really usable because the index needs to be less than the count (which is zero). These extra properties are added invisibly, that is, they do not show up in the output of Get-Member, but they can still be evaluated, as the following illustrates: