Topic: Best Way to Filter the Results of Your Commands
Notice: This post is a part of the PowerShell Monday series — a group of quick and easy to read mini lessons that briefly cover beginning and intermediate PowerShell topics. As a PowerShell enthusiast, this seemed like a beneficial way to ensure those around me at work were consistently learning new things about Windows PowerShell. At some point, I decided I would share these posts here, as well. Here’s the PowerShell Monday Table of Contents.
Today we’re going to discuss the best way to filter the results of our commands. These first two commands return the same results — the services on the local computer that begin with the letter L. Let’s discuss why one is better than the other, right after we take a look at the commands.
Get-Service | Where-Object Name -like L* # And Get-Service -Name L*
While piping the results of one command to another is beneficial in Windows PowerShell, it’s not always the best idea. If a cmdlet has a way to filter your results without the need to pipe it to a filtering cmdlet, such as Where-Object, then use it. The reason this is faster is because in the first above example we return ALL the services first and then filter them one by one. In the second example, we filter immediately, which means we never stop to consider services that start with any other letter than L. You might someday hear this called the filter left rule. The idea is to do as much filtering as close to the left of your commands as possible. You might also here it as filter left, format right. This is to say that formatting cmdlets (Format-Table, Format-List, etc.) should be as far to the right of a command(s) as possible (typically the last cmdlet in a command that includes piping).
Here’s an example from Active Directory (AD). In the first command we return EVERY AD user object and then filter them, whereas in the second command we filter immediately and only process the users we want to return. In the case of AD, it’s not only faster, but it’s going to be less resource intensive than peering at every AD user object in the entire domain, to only return what might be a handful of users.
Get-ADUser -Filter * | Where-Object Surname -eq 'Smith' # And Get-ADUser -Filter {Surname -eq 'Smith'}
I ran the two above commands through the Measure-Command cmdlet in order to measure their execution times. I did this around 10 p.m. Saturday evening using my last name. The first command, that checked every AD user object, took 6 1/2 minutes to complete. The second command took an average of 30 milliseconds. That’s a big difference.
Bonus: Here’s how to determine which cmdlets have a specific parameter. In this case, we’re checking for cmdlets that include a -Filter parameter. The below image only shows the first nine cmdlets of the sixty found on my computer that include this parameter. Remember, however, that in the case of the Get-Service cmdlet, that we didn’t filter with a -Filter parameter. In that instance, we used the -Name parameter, as it accepted wildcards. Use Get-Help to determine if a cmdlet’s parameters accept wildcards, or not.
Get-Command -ParameterName CommandType CommandType Name ModuleName ----------- ---- ---------- Cmdlet Add-Content Microsoft.PowerShell.Management Cmdlet Clear-Content Microsoft.PowerShell.Management Cmdlet Clear-Item Microsoft.PowerShell.Management Cmdlet Clear-ItemProperty Microsoft.PowerShell.Management Cmdlet Copy-Item Microsoft.PowerShell.Management Cmdlet Copy-ItemProperty Microsoft.PowerShell.Management Cmdlet Get-ADAuthenticationPolicy ActiveDirectory Cmdlet Get-ADAuthenticationPolicySilo ActiveDirectory Cmdlet Get-ADCentralAccessPolicy ActiveDirectory
That’s all. We’ll be back at it next Monday.