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: