In Learn PowerShell Scripting in a Month of Lunches, you'll discover how scripting is different from command-line PowerShell, as you explore concrete hands-on examples. You'll master good habits for elegant, efficient, and error-free scripting along with best practices and easy-to-digest tips that any professional PowerShell scripter needs to know. You'll also get to grips with the process of developing, testing, and deploying scripts and tackle the art of PowerShell toolmaking.
Save 42% off with code sljones at: https://www.manning.com/books/learn-powershell-scripting-in-a-month-of-lunches
Learn PowerShell Scripting in a Month of Lunches: use PowerShell how it wants to be used
1. Parameter Binding and
the PowerShell Pipeline
Save 42% off Learn PowerShell Scripting in
a Month of Lunches with code sljones at
manning.com.
2. The PowerShell Pipeline
As you are likely aware, PowerShell has a strong ability to create highly
reusable, context-independent tools, which it refers to as commands.
Toolmaking is scripting these commands together to do wonderful
things. To be a toolmaker is to understand the pipeline at its most basic
level, and to create tools that leverage the pipeline.
In this presentation we’re going to focus on the pipeline as something
to write for, rather than to simply use.
3. The PowerShell Pipeline
Take traditional pipeline behavior from shells like Bash and Cmd.exe.
Mix in PowerShell’s unique object-oriented nature. Add a dash of Linux-
style command parsing.
The result?
PowerShell’s pipeline, a fairly complex and deeply powerful tool for
composing tools into administrative solutions.
5. Visualizing the Pipeline
Now, write some command names in those boxes. Maybe Get-
Process in the first box, maybe ConvertTo-HTML in the second box,
and perhaps Out-File in the third box.
Get-Process ConvertTo-HTML Out-File
6. Visualizing the Pipeline
The image on the previous slide is a good visual depiction of how
PowerShell runs commands in the pipeline: as one command produces
objects, they go into the pipeline one at a time, and get passed on to
the next command.
At the end of the pipeline, when there are no further commands, any
objects in the pipeline are passed to PowerShell’s formatting system to
be formatted for on-screen display.
But how is PowerShell making this happen?
7. It’s all in the parameters
PowerShell uses two methods to dynamically figure out how to get
data – that is, objects – out of the pipeline and “into” a command.
Both of these methods rely on the accepting command’s parameters. In
other words – and this is important – the only way a command can
accept data is via its parameters.
8. It’s all in the parameters
This implies when you design a command, and you design its
parameters, you are deciding how that command will accept
information, including how it will accept information from the pipeline.
This process is therefore not magic; it’s a science, and it’s decided in
advance by whoever designed the command.
Let’s take a closer look!
9. Plan A: ByValue
PowerShell has a hardcoded preference to pass entire objects from the
pipeline into a command. Because of that hardcoded preference, it will
always attempt to do that before it tries to do anything else. In order to
do so, the accepting command must:
• Define a parameter that supports accepting pipeline input ByValue…
• …and that parameter must be capable of accepting whatever type of
object happens to be in the pipeline.
10. Plan A: ByValue
Let’s run Get-Process from our exercise on slide 5.
When we run Get-Process | Get-Member – the first line of output
will contain the TypeName, which identifies the kind of object that the
command produced. Turns out it’s a System.Diagnostics.Process
object.
What about the second command from slide 5?
11. Plan A: ByValue
You’ll want to first make sure you’ve run Update-Help so that you have
help files, and then run Help ConvertTo-HTML –ShowWindow so that
you can explore the complete help. Do you see any parameters of the
command that are capable of accepting a [Process] object? Probably
not.
However, you probably will see a parameter capable of accepting an
[Object] (or [Object[]]).
12. Plan A: ByValue
In the Microsoft .NET Framework, System.Object is like the mother
type for everything else. That is, everything inherits from the Object
type. In PowerShell, PSObject is more or less equivalent to Object. So,
whenever you see that a parameter accepts PSObject, you know that
it can accept basically anything. In ConvertTo-HTML, you’ll find an –
InputObject parameter, which fulfills our two criteria:
• It can accept pipeline input using the ByValue technique
• It can accept objects of the type System.Diagnostics.Process,
because it can accept the more-generic PSObject
13. Plan A: ByValue
Therefore, PowerShell will take the output of Get-Process, and attach
it to the –InputObject parameter of ConvertTo-HTML. Reading the
help for the second command, that parameter “specifies the objects to
be represented in HTML.” So, whatever you pipe into ConvertTo-HTML
will be picked up, ByValue, by the –InputObject parameter, and will
be “represented in HTML.”
Try it yourself and see!
14. Introducing Trace-Command
PowerShell actually has a way for you to see this passing-of-the-objects
happening. It’s called Trace-Command, and it’s a really useful way for
debugging pipeline parameter binding. It’ll show you, in detail, the
decisions PowerShell is making, and the actions it’s attempting to take.
To run the command, you’ll run something like Trace-Command –Name
parameterbinding –Expression { Your command goes here } –
PSHost.
Keep in mind that your command will actually run, so you need to be
careful not to run anything that could be damaging, like deleting a
bunch of user accounts just to see what happens!
15. Tracing ByValue Parameter Binding
Let’s go ahead and apply Trace-Command to our current example.
Here’s the command we ran:
PS C:> trace-command -Expression { get-process | convertto-html |
[CA]out-null } -Name ParameterBinding –PSHost
You’ll notice that we ended our command with Out-Null; we did that
to suppress the normal output of ConvertTo-HTML, just to keep the
output a little cleaner. You will, however, see PowerShell dealing with
getting objects from ConvertTo-HTML into Out-Null, so it’s a useful
illustration.
16. Tracing ByValue Parameter Binding
You’ll first see PowerShell attempt to bind – that is, “attach” – any
NAMED arguments for Get-Process. There weren’t any – we didn’t
specify any parameters manually in our command.
DEBUG: ParameterBinding Information: 0 : BIND NAMED cmd line args
[Get-Process]
PowerShell next looks for POSITIONAL parameters, which we also
didn’t have.
17. Tracing ByValue Parameter Binding
PowerShell then checks to make sure that all of the command’s
MANDATORY parameters have been provided, and we pass that check.
DEBUG: ParameterBinding Information: 0 : BIND POSITIONAL cmd lineargs
[Get-Process]
DEBUG: ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on
cmdlet [Get-Process]
This whole process – named, positional, and then a mandatory check –
repeats for the ConvertTo-HTML and Out-Null commands.
18. Tracing ByValue Parameter Binding
There’s an important lesson in this: regardless of how a command is
wired up to accept pipeline input, specifying named or positional
parameters always takes precedence, because PowerShell binds those
first.
If we’d manually specified –InputObject, for example, then we’d have
prevented the ByValue parameter binding from working, because we’d
have “bound up” the parameter ourselves before ByValue was even
considered.
This is shown on the next slide.
19. Tracing ByValue Parameter Binding
DEBUG: ParameterBinding Information: 0 : BIND NAMED cmd line args
[ConvertTo-Html]
DEBUG: ParameterBinding Information: 0 : BIND POSITIONAL cmd line args
[ConvertTo-Html]
DEBUG: ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on
cmdlet [ConvertTo-Html]
DEBUG: ParameterBinding Information: 0 : BIND NAMED cmd line args
[Out-Null]
DEBUG: ParameterBinding Information: 0 : BIND POSITIONAL cmd line args
[Out-Null]
DEBUG: ParameterBinding Information: 0 : MANDATORY PARAMETER CHECK on
cmdlet [Out-Null]
20. Tracing ByValue Parameter Binding
The next thing that happens is PowerShell calling each of the three
commands’ BEGIN code. Not all commands specify any BEGIN code, but
PowerShell gives them all the opportunity.
DEBUG: ParameterBinding Information: 0 : CALLING BeginProcessing
DEBUG: ParameterBinding Information: 0 : CALLING BeginProcessing
Now, this next bit it a little surprising, because PowerShell is attempting
to bind a pipeline object to a parameter of Out-Null.
How the heck did anything even get into the pipeline at this point?
21. Tracing ByValue Parameter Binding
Well, the previous command, ConvertTo-HTML, has clearly taken the
opportunity to produce some output from its BEGIN code.
Sneaky.
Anyway, PowerShell now has to deal with that, even though the first
command, Get-Process, hasn’t even run yet!
DEBUG: ParameterBinding Information: 0 : BIND PIPELINE object to
parameters: [Out-Null]
22. Tracing ByValue Parameter Binding
This next bit is interesting. Here’s what you’ll see:
• PowerShell identifies the type of object in the pipeline as a
System.String. Take a minute and read the full help for Out-Null. Do
you see any parameters capable of accepting a String from the
pipeline using the ByValue method?
DEBUG: ParameterBinding Information: 0 : PIPELINE object TYPE =
[System.String]
DEBUG: ParameterBinding Information: 0 : RESTORING pipeline
parameter's original values
23. Tracing ByValue Parameter Binding
PowerShell is about to discover that the –InputObject parameter of
Out-Null accepts either Object or PSObject, and so it’s going to bind
the output of ConvertTo-HTML to that –InputObject parameter.
DEBUG: ParameterBinding Information: 0 : Parameter
[InputObject] PIPELINE INPUT ValueFromPipeline NO COERCION
DEBUG: ParameterBinding Information: 0 : BIND arg [<!DOCTYPE
html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">] to parameter
[InputObject]
DEBUG: ParameterBinding Information: 0 : BIND arg
[<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">] to param
[InputObject] SUCCESSFUL
24. Tracing ByValue Parameter Binding
In fact, it appears to have accepted a couple of String objects from the
pipeline.
These look like header lines for an HTML file, which makes sense –
ConvertTo-HTML probably gets these out of the way as “boilerplate”
before it settles down to its real job.
On the next slide, we see that the MANDATORY check on Out-Null
succeeds, and we continue to deal with initial boilerplate issued by
ConvertTo-HTML.
26. Tracing ByValue Parameter Binding
OK, let’s get ahead of our selves a bit, past all the boilerplate
“header” HTML. We’ll go down to the point where Get-Process
actually runs, and where PowerShell recognizes the type of object
it’s produced:
DEBUG: ParameterBinding Information: 0 : BIND PIPELINE object to
parameters: [ConvertTo-Html]
DEBUG: ParameterBinding Information: 0 : PIPELINE object TYPE =
[System.Diagnostics.Process#HandleCount]
27. Tracing ByValue Parameter Binding
Next we’ll see those Process objects being bound to the –
InputObject parameter of ConvertTo-HTML:
DEBUG: ParameterBinding Information: 0 : Parameter [InputObject]
PIPELINE INPUT ValueFromPipeline NO COERCION
DEBUG: ParameterBinding Information: 0 : BIND arg
[System.Diagnostics.Process] to parameter [InputObject]
DEBUG: ParameterBinding Information: 0 : BIND arg
[System.Diagnostics.Process] to param [InputObject] SUCCESSFUL
28. Tracing ByValue Parameter Binding
The trace output goes on, of course, but this is really what we
were looking for: proof that PowerShell is doing what we
expected. You’ll notice the phrase NO COERCION quite a bit in the
preceding; that’s an indication that PowerShell was able to bind
the output as-is, without trying to convert it to something else.
Coercion is one of the things that can make pipeline parameter
binding more confusing, and it’s what this trace output can help
you see and understand. For example, PowerShell is capable of
coercing, or converting, a number into a string, so that the
resulting string can bind to a parameter that accepts String.
29. When ByValue Fails
So that’s the ByValue story.
But what if it fails?
Go back to your paper diagram. Erase or cross-out ConvertTo-
HTML and Out-Null, and in the second box, write Stop-Service.
Don’t run the resulting command, yet – we need to talk about
what happens.
30. When ByValue Fails
We know that the first command produces Process objects.
Examining its full help file, do you see any parameters of Stop-
Service which will:
• Accept pipeline input ByValue…
• …and also accept input types of either Process, Object, or
PSObject?
We don’t see any parameters that fit the criteria, and so the
ByValue method fails.
Time for Plan B – but that’s a topic for a different day.
31. Get up to speed with PowerShell
during your lunch break!
Save 42% off Learn PowerShell Scripting in
a Month of Lunches with code sljones at
manning.com.
Also see: