Sorry if this question is already been answered, I could find similar questions but not the exact one I need to ask.
Let's take two examples:
1. Get-Process -name msedge,putty | Stop-Process
2. Get-Process -name msedge,putty | Foreach-Object {Stop-Process $_}
Both are doing the same operation. What about the methods used in each one? Are they the same in the sense that the first example just omits the Foreach-Object
construction for the sake of code readability/aesthetics?
The first example requires the Cmdlet to support binding of the relevant parameters via the pipeline. In your case Stop-Process
will bind the Process object from the pipeline to it's -InputObject
parameter.
You can check that using get-help stop-process -Parameter *
and see which parameters have "Accept pipeline input?" set to true.
In case a Cmdlet does not support the binding of the relevant parameters values you can wrap ForEach-Object
around it, like you did in the second example. This way you use the automatic variable $_
to bind the current pipeline object (or information that you derive from it) "manually" to the corresponding parameter.
What approach should you use if a Cmdlet supports the binding of parameter values from the pipeline? That unfortunately depends. It is possible to write a Cmdlet that behaves differently, depending on how the parameter values are bound. Let me illustrate this point:
function Test-BindingFoo {
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline)]
[string[]]
$InputParameter
)
begin {
Write-Host "[BEGIN]"
}
process {
foreach ($value in $InputParameter) {
Write-Host "The current value is: $value"
}
}
end {
Write-Host "[END]"
}
}
If you execute this Cmdlet using the pipeline binding the Begin block of the function is executed exactly once:
❯ "foo1", "foo2" | Test-BindingFoo
[BEGIN]
The current value is: foo1
The current value is: foo2
[END]
If you use ForEach-Object
the Begin block is executed every time an object passes through the pipeline:
❯ "foo1", "foo2" | ForEach-Object { Test-BindingFoo $_ }
[BEGIN]
The current value is: foo1
[END]
[BEGIN]
The current value is: foo2
[END]
In well implemented Cmdlets the difference here should not matter. But I found it useful to be aware of what happens inside a Cmdlet when parameteres are passed in in the ways that we have discussed here.