Sunday, November 07, 2010

PowerShell: Advanced Functions (The Pipeline)

How do we handle items passed to our Advanced Function from the PowerShell pipeline?

When we run a CmdLet or a piece of core that outputs objects we can pass this output to another CmdLet as long as the CmdLet is designed to receive the kind of objects we are passing to it.

Let’s set up our Function to receive any object passed in the pipeline and look at the objects that are received.  First we will inspect teh default object in the pipeline using the $_ variable.

function Test-MyCmdLet{
    Param(
        [cmdletBinding()]
        [parameter(ValueFromPipeline=$true)]$stuff
        )
    process{
        ($_.GetType()).Name
    }
}

What do we get as output when running the following?

1. dir c:\ | Test-MyCmdLet
2. dir c:\ | where-object {!$_.psiscontainer} | Test-MyCmdLet
3. Get-Process | Test-MyCmdLet

In case #1 above we get a mixture of object types.  In case #2 we have limited the output to one type by filtering out objects that are containers (folders).  In case #3 we get only ‘Process’ objects.

You can see that our CmdLet can receive any object and perform an inspection or nay other code task we may need to perform on the object.  In this case we are extracting the object’s type-name and passing that into the pipeline.

What happens if we inspect our parameter $stuff using this new version of the CmdLet?

 function Test-MyCmdLet{
         Param(
         [cmdletBinding()]
         [parameter(ValueFromPipeline=$true)]
         $stuff
     )
     process{
         $stuff
     }
 }

Run the same three test examples from above to see how this acts.  Notice that everything being passed into the CmdLet is just being passed back out. 

What does this look like if we use the following version to inspect the objects type?

 function Test-MyCmdLet{
         Param(
         [cmdletBinding()]
         [parameter(ValueFromPipeline=$true)]
         $stuff
     )
     process{
         $stuff.GetType().Name
     }
 }

This looks the same as using the default object.  What good is that?  Well it can be useful however, we really would like to use the parameters for something more useful since we always have access to the full object. Why don’t we ‘bind’ the parameter to our input object?

function Test-MyCmdLet(
        [parameter(
            ValueFromPipelineByPropertyName=$true
            )]$Name
        ){
    process{
        $Name
    }
}

Now we have changed the attribute to ValueFromPipelineByPropertyName.  This will cause the name parameter to be connected to any object property with the 'name ‘Name’.  (not case sensitive).  Run the three examples again and see what happens.

We can use this to bind to one or more properties by name.  This is one of the main and most powerful features of the pipeline.

Previous: Script CmdLets
Next: Binding Behavior

No comments:

Post a Comment