ForEach-Object is a powerful pipeline cmdlet, but foremost it is an anonymous function. ForEach-Object is great to build "prototype functions" yet once you find a particular ForEach-Object statement useful, you might want to turn it into a reusable function.
The following statement uses ForEach-Object to count the elements emitted by a previous command. It uses the streaming mechanism (rather than wasting memory by first collecting all results in a variable):
Get-Service | ForEach-Object -Begin { $i = 0} -Process { $i++ } -End { $i }
To turn this dynamic statement into a function, simply translate it like this:
function Get-Count { Begin { $i = 0} Process { $i++ } End { $i } } Get-Service | Get-Count
As you can see, the -Begin, -Process, and –End parameters are simply mapped to their respective script blocks inside the function. You can even continue to use $_ inside your function to access the streaming element. This statement returns only running services:
Get-Service | ForEach-Object { if ($_.Status -eq 'Running') { $_ } }
And here is the respective function for it:
function Get-RunningService { process { if ($_.Status -eq 'Running') { $_ } } } Get-Service | Get-RunningService
Why would you want to turn these pipeline statements into functions? Because it makes your code much more readable.
Pipeline cmdlets like ForEach-Object and Where-Object are perfectly fine if what you do is a very unique scenario. Once you realize, though, that the functionality you use could be useful in other scenarios as well, you might want to turn your ad-hoc pipeline cmdlets into a reusable pipeline-aware function.