I want to stop a service named "ALG" so I use: "alg" | stop-service
It works.
Get-help stop-service -parameter name
says:
Pipeline input:true(ByPropertyName, ByValue)
and "alg" is "ByPropertyValue" right?
I want to stop a process named notepad so I use: "notepad" | stop-process
and I get an error.
Get-help stop-process -parameter name
says: Pipeline input true(ByPropertyName)
and "notepad" is "ByPropertyName"?
Why this error?
Mathias R. Jessen's answer provides a solution for piping strings (process names) to Stop-Process
.
js2010's answer has the correct explanation for why piping strings to Stop-Process
doesn't work (without extra effort), and also offers a helpful technique for tracing parameter binding, but - as of this writing - the answer contains incidental information that confuses the issue a bit.
Let me offer a more detailed explanation:
Stop-Process
, unlike Stop-Service
, is not designed to accept strings (process names) as pipeline input.
While string input in the abstract can still work, namely if the strings can automatically be converted to one of the data types expected by a command's ByValue
(whole-object) pipeline-binding parameters, this is not the case with Stop-Process
, because a process name (string) cannot (automatically) be converted to a System.Diagnostics.Process
instance (-InputObject
)[1].
When PowerShell considers binding pipeline input to parameter -Name
during a call, it looks for an object with a Name
property, because the parameter declaration specifies that pipeline input is accepted only if the input object has a property named for the parameter:
In help topics, such as the one for Stop-Process
, this is expressed as ByPropertyName
.
In code, it is expressed as the Boolean ValueFromPipelineByPropertyName
property of the System.Management.Automation.ParameterAttribute
type; that is, expressed in PowerShell code, the parameter declaration looks something like: Note that ValueFromPipelineByPropertyName
is short for ValueFromPipelineByPropertyName = $true
[Parameter(ValueFromPipelineByPropertyName)] [string[]] $Name
A [string]
(System.String
) instance such as "alg"
doesn't have a Name
property - it is itself the name.
Therefore, in the absence of an automatic conversion[1] to the System.Diagnostics.Process
type of the only ByValue
parameter, -InputObject
, and in the absence of Name
and Id
properties for the ByPropertyValue
parameters, invocation fails with the following error message, which in essence tells you that the pipeline input is invalid (cannot be bound to any parameters):
The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.
Stop-Service
, by contrast, is designed to accept string input, because its -Name
parameter is (also) declared as accepting strings directly as input objects, as a whole.
In help topics, such as the one for Stop-Service
, this is expressed as ByValue
.
In PowerShell code, it is expressed as ValueFromPipeline
:
[Parameter(ValueFromPipeline)] [string[]] $Name
Note:
While a given parameter can be both ByValue
and ByPropertyValue
- which is indeed the case for Stop-Service
's -Name
parameter - that isn't typical.
Typically, pipeline-binding parameters are declared as scalars rather than arrays (e.g., for Sort-Object
, -InputObject <PSObject>
rather than -InputObject <PSObject[]>
), which means that passing multiple arguments is only supported via the pipeline, not by direct argument - see GitHub issue #4242 for background information.
PS> Get-Help Stop-Process -Parameter Name
-Name <String[]>
Specifies the process names of the processes to stop. You can type multiple process names, separated by commas, or use wildcard characters.
Required? true
Position? named
Default value None
Accept pipeline input? True (ByPropertyName)
Accept wildcard characters? true
Note the Accept pipeline input?
line; for a non-pipeline-binding parameter, you'd see False
in the second column.
PS> Get-Help Stop-Process -Parameter * | Where pipelineInput -like True*
-Id <Int32[]>
Specifies the process IDs of the processes to stop. To specify multiple IDs, use commas to separate the IDs. To find the PID of a process, type
`Get-Process`.
Required? true
Position? 0
Default value None
Accept pipeline input? True (ByPropertyName)
Accept wildcard characters? false
-InputObject <Process[]>
Specifies the process objects to stop. Enter a variable that contains the objects, or type a command or expression that gets the objects.
Required? true
Position? 0
Default value None
Accept pipeline input? True (ByValue)
Accept wildcard characters? false
-Name <String[]>
Specifies the process names of the processes to stop. You can type multiple process names, separated by commas, or use wildcard characters.
Required? true
Position? named
Default value None
Accept pipeline input? True (ByPropertyName)
Accept wildcard characters? false
Note: The above technique gleans the parameter information from the MAML-based help file that may accompany a given cmdlet (most built-in cmdlets do come with such help files). While the information in the help file should correctly reflect the cmdlet's actual parameter definitions and typically does, it isn't guaranteed to. When in doubt, use the following technique instead, which directly examines a cmdlet's actual definition:
# Lists all pipeline-binding parameters defined by the given cmdlet,
# by examining the actual cmdlet definition.
(Get-Command Stop-Process).ParameterSets.Parameters |
Where-Object { $_.ValueFromPipeline -or $_.ValueFromPipelineByPropertyName} |
Select-Object -Unique Name, Aliases, ParameterType, @{
Name = 'Accepts pipeline input'
Expression = {
'True ({0})' -f ($(
if ($_.ValueFromPipeline) { 'ByValue'}
if ($_.ValueFromPipelineByPropertyName) { 'ByPropertyName' }
) -join ', ')
}
} | Sort-Object Name
[1] Unless a parameter is declared to support a custom type conversion via a System.Management.Automation.ArgumentTransformationAttribute
-derived attribute (which is uncommon), PowerShell's usual conversion rules apply here, which employ several techniques, discussed in this answer. In the case of System.Diagnostics.Process
, conversion from a string isn't possible, because the target type neither has a single-argument constructor that has a string, nor does it have a static .Parse()
method. A quick test for convertibility is to attempt a cast: [System.Diagnostics.Process] 'notepad'
fails. By contrast, [System.ServiceProcess.ServiceController] 'alg'
works, because that type does have a single-parameter constructor that accepts a string, but note that this conversion does not come into play during parameter binding in a call to Stop-Service
such as 'alg' | Stop-Service
- there, the string is bound as-is to the ByValue
-Name
parameter.