Summary: Microsoft Scripting Guy, Ed Wilson, talks about piping the results from one cmdlet to another.
Hey, Scripting Guy! I have a problem. When I pipe information from one Windows PowerShell cmdlet to anther (for example, when I use Get-Process and pipe it to Stop-Process ), sometimes it works, and sometimes it does not. This is not great. What is the deal? Can you help?
—LR
Hello LR,
Microsoft Scripting Guy, Ed Wilson, is here. The nice thing about a hurricane is when it is gone. In Charlotte, we got some pretty heavy rain as a result of Hurricane Arthur, but nothing serious. Now we are enjoying the cool weather that followed that event. It seems like autumn outside.
LR, using the Windows PowerShell pipeline is not really like a hurricane, but sometimes it seems unpredictable. Therefore, things can appear to go around in circles. The key to knowing what is going on is understanding parameter sets.
For example, if I use Start-Process, I can easily start a new process. I can then use Get-Process to retrieve that process. This is shown here:
PS C:\> Start-Process notepad
PS C:\> get-Process notepad
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
84 8 1508 8112 110 0.08 5412 notepad
It would seem to make sense that I can now pipe the results to the Stop-Process cmdlet and stop the process. But if I do this, I get the following error message:
PS C:\> get-Process notepad | Stop-Process notepad
Stop-Process : Cannot bind parameter 'InputObject'. Cannot convert the "notepad" value
of type "System.String" to type "System.Diagnostics.Process".
At line:1 char:36
+ get-Process notepad | Stop-Process notepad
+ ~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Stop-Process], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Comman
ds.StopProcessCommand
The key is to realize this is not a “bogus error.” Rather, there is significant information here. It tells me that the cmdlet tries to bind the parameter InputObject, but that it does not work because “notepad” is a string and not a process object.
Three parameter sets
The Stop-Process cmdlet accepts the following three parameter sets:
PS C:\> Get-Command Stop-Process -Syntax
Stop-Process [-Id] <int[]> [-PassThru] [-Force] [-WhatIf] [-Confirm] [<CommonParameters>]
Stop-Process -Name <string[]> [-PassThru] [-Force] [-WhatIf] [-Confirm]
[<CommonParameters>]
Stop-Process [-InputObject] <Process[]> [-PassThru] [-Force] [-WhatIf] [-Confirm]
[<CommonParameters>]
The default parameter set is ID, which accepts an integer in the first position. To find this information about parameter sets, use the Get-Command cmdlet and pipe the output to Select-Object while expanding the ParameterSets parameter.
Here is a sample of the output for the first parameter (ID). The output shows that ID is the default parameter set, and that ID appears in the first position and is mandatory. It also tells us that this parameter does not accept a value from the pipeline.
PS C:\> Get-Command Stop-Process | select -expand parametersets
Parameter Set Name: Id
Is default parameter set: True
Parameter Name: Id
ParameterType = System.Int32[]
Position = 0
IsMandatory = True
IsDynamic = False
HelpMessage =
ValueFromPipeline = False
ValueFromPipelineByPropertyName = True
ValueFromRemainingArguments = False
Aliases = {}
Attributes =
System.Management.Automation.ParameterAttribute
What about the Name parameter? Well, there is a Name parameter set. It is not the default parameter set, and it also does not accept a value from the pipeline. This is shown here:
Parameter Set Name: Name
Is default parameter set: False
Parameter Name: Name
ParameterType = System.String[]
Position = -2147483648
IsMandatory = True
IsDynamic = False
HelpMessage =
ValueFromPipeline = False
ValueFromPipelineByPropertyName = True
ValueFromRemainingArguments = False
Aliases = {ProcessName}
Attributes =
System.Management.Automation.AliasAttribute
System.Management.Automation.ParameterAttribute
So, when I try to tell the Stop-Process cmdlet that I want to stop the Notepad process, it does not work because it recognizes that “notepad” is not a number, so we are not using the default parameter set. Also it is not using the Name parameter set because it does not accept the pipelined input. So, how can it work?
That is easy! Remove the word Notepad, and it works just fine. I know this will work because of the third parameter set, which accepts pipelined input:
Parameter Set Name: InputObject
Is default parameter set: False
Parameter Name: InputObject
ParameterType = System.Diagnostics.Process[]
Position = 0
IsMandatory = True
IsDynamic = False
HelpMessage =
ValueFromPipeline = True
ValueFromPipelineByPropertyName = False
ValueFromRemainingArguments = False
Aliases = {}
Attributes =
System.Management.Automation.ParameterAttribute
So, all I need to do is to pipe a process object to the Stop-Process cmdlet, and it will stop that process. I can see what process it stops by using the –PassThru parameter, as shown here:
PS C:\> get-Process notepad | Stop-Process -PassThru
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
84 8 1488 8112 98 0.08 5412 notepad
LR, that is all there is to using the pipeline. Poshpourri Week will continue tomorrow when I will talk about more cool stuff.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy