I’m trying to decide how to write certain kinds of pipelines to achieve the best clarity, while not changing the functionality. Specifically, when using ForEach-Object in the middle of a pipeline. Here’s some example code I posted in another thread:
Import-Csv -Path test.csv |
ForEach-Object {
$emailAddress = Get-MailBox $_ | Select-Object -ExpandProperty EmailAddresses | Select-Object -First 1 -ExpandProperty SmtpAddress
if ($emailAddress -ne $null)
{
Add-Member -InputObject $_ -MemberType NoteProperty -Name EmailAddress -Value $emailAddress -PassThru
}
} |
Export-Csv -Path new.csv -NoTypeInformation
The last two lines (‘} |’ followed by another cmdlet) look ugly to me. It’s easy to overlook that the end of the ForEach-Object block is being piped to that second command, rather than the next cmdlet just being executed after the ForEach block completes. Even putting the Export-Csv command on the same line as the closing brace and pipe character doesn’t seem like much of an improvement (and breaks the pattern of starting a new line after each pipe symbol in the rest of the pipeline).
You could write a filter (or function), and call that from inside the pipeline instead (maybe separating the code that adds the EmailAddress property from the code that eliminates rows for users where the email address is null, for further clarity enhancements), something like this:
# I couldn't decide on a name for a filter that both adds the email address column and
# removes records that don't contain an email address. Sort of violates the "one command,
# one task" guideline.
Import-Csv -Path test.csv |
SomeUsefulFilterName |
Export-Csv -Path new.csv -NoTypeInformation
# Separating the tasks makes the filter easier to name, and the pipeline easier to understand at a glance
# (though at a slight performance cost):
Import-Csv -Path test.csv |
Add-EmailAddressProperty |
Where-Object { $_.EmailAddress -ne $null } |
Export-Csv -Path new-csv -NoTypeInformation
Both of those are definitely easier to read than the first code, but you’d have to go hunting for your filter / function if you wanted to read all of the actual code involved in the pipeline, instead of having it all right there at a glance.
On a side note, the filter will get much better performance than ForEach-Object, if that’s a concern at some point. Right now I’m just concerned with code clarity, though. If you had to take over maintenance or development of some PowerShell code that you’ve never seen before, which style would you prefer to read? Do you like having all of the code in the ForEach-Object block right there with the rest of the pipeline (even if it looks a bit ugly in the middle of the pipeline like that), or do you prefer moving that code out into a filter somewhere, and having a more tidy pipeline calling it?