How to use Where-Object in PowerShell to filter everything

Brock Bingham candid headshot
Brock Bingham|January 30, 2023
Where-Object featured image
Where-Object featured image

One of PowerShell’s most valuable functions is its ability to retrieve data. But unfiltered data can be overwhelming and chaotic. The Where-Object cmdlet in PowerShell is designed to help users filter and manipulate data, turning excessive details into valuable information. Today, we'll delve into the depths of unfiltered data and discover how to transform it into actionable information by utilizing the Where-Object cmdlet in PowerShell.

The value of filtered data

Network environments are overflowing with data. IP addresses, usernames, services, applications, configurations, attributes, and more are all data points that sysadmins can leverage to make informed decisions, automate processes, and take action.

However, while PowerShell is great at retrieving information, it doesn’t always return it in the most usable format. In fact, PowerShell often returns much more data than is needed.

For example, if I was looking for a specific file, I could run Get-ChildItem -Recurse against my entire folder directory (please don’t do this), but that would return thousands of results, and I wouldn’t be any closer to locating the file I’m looking for. Instead, I can use the Where-Object cmdlet to filter against properties, such as the name, size, location, and date, significantly narrowing down the search results.

What does the Where-Object cmdlet do?

The Where-Object cmdlet in PowerShell is a filtering mechanism. You can use Where-Object to filter collections from preceding commands using specific criteria. Objects that meet the conditions of the filter then pass through the pipeline to the next cmdlet.

Where-Object is often preceded by other commands, such as Get-ChildItem, Get-Process, and Get-AppxPackage, but can also be preceded by other arrays and hashtables.

How to structure a Where-Object command

Using the Where-Object cmdlet is pretty straightforward, but some nuances can trip up newcomers.

As mentioned above, Where-Object is usually preceded by another command and followed by a qualifying filter, like this:

<cmdlet> | Where-Object -Property <property_name> <operator> <filter>

Here’s a real-world example that is probably a bit easier to follow:

Get-Process | Where-Object -Property Name -eq ‘Notepad’
A PowerShell script that returns the Notepad process using the Where-Object filtering cmdlet.

In this example, you can really start to see the structure of the command. It begins with the preceding cmdlet Get-Process. The results of Get-Process are piped to the Where-Object cmdlet. Where-Object is followed by the filter criteria. In this case, we have the -Property parameter followed by the property Name, the -eq equality operator, and the filter ‘Notepad.

Something to watch for that can change the look of a Where-Object command is the use of aliases and positional parameters.

There are two aliases for the Where-Object cmdlet: ? and Where. Additionally, the -Property parameter is positional, which means users don’t even have to include the parameter, just the parameter value. Here are a couple of examples of how the above command could be written and still return the same results.

Get-Process | Where Name -eq ‘Notepad’
Get-Process | ? Name -eq ‘Notepad’
Comparison of 3 different scripts that all perform the same function.

These examples are the same as the initial command, but they take advantage of aliases and positional parameters to shorten the overall command length. Aliases are great for reducing script length, but they can make it more difficult for beginners to understand what the script is doing.

How to utilize script blocks for more advanced PowerShell filtering

Script blocks are collections of statements contained in braces. They are similar to functions but don’t require a name.

Script blocks can be used in conjunction with the Where-Object cmdlet. They are especially useful when you need more advanced filtering options. Script blocks allow users to add multiple filters to the same Where-Object statement.

Here is a basic example of a Where-Object statement using a script block.

Get-Service | Where-Object -FilterScript {$_.Status -eq 'Stopped' -and $_.StartType -eq 'Automatic'} | Select-Object Name, Status, StartType
A PowerShell script that filters for specific services.

While this example is a bit longer than the first, there are only a few things I need to point out to help you understand what’s going on.

First off, pretend the whole section following the last pipe doesn’t exist. I added that to show the StartType property in the returned results because it’s not returned by default. It’s not necessary for the command to function.

Next, notice that we replaced the -Property parameter with the -FilterScript parameter. This ensures we use the correct parameter set to use a script block. However, -FilterScript is a positional parameter, and many users leave it off, just as we left off the -Property parameter in the first set of examples.

You may also wonder about the $_. symbols. These are automatic variables called $PSItems. They act as the variable for the current pipeline input item being processed. What’s important is the property following the symbol. $_.Status uses the Status property for the filter, and $_.StartType uses the StartType property.

Lastly, we combined the two filters with the -and operator.

How to find files with Where-Object

Let’s look at an example where we utilize Where-Object to find files matching specific filter criteria. For this example, we’ll search for .jpg and .mov files that are larger than 10 MB and were modified within the last 10 days.

Get-ChildItem -Path 'C:\Users\' -Include '*.jpg', '*.mov' -Recurse | Where-Object -FilterScript {$_.Length -gt 10MB -and $_.LastWriteTime -gt (Get-Date).AddDays(-10)}
Filtering for files using a PowerShell script.

In this example, we utilize the -Path parameter to narrow down the search directories. We use the -Include parameter filter for files matching the .jpg and .mov string patterns. In the script block, we add the $_.Length property and set it to filter for files greater than (-gt) 10MB. Lastly, we add the $_.LastWriteTime property and compare it to the current date minus 10 days.

Filter left to increase performance

Filter left is the concept that items should be filtered as early as possible in the command to limit the number of results passed through the pipeline, increasing performance. While it’s not always possible to filter before the Where-Object cmdlet, many commands provide filterable parameters. A common parameter used to filter at the beginning of the pipeline is the -Name parameter. Here’s an example:

Get-Service -Name 'Wi*' | Where-Object -FilterScript {$_.Status -eq 'Running'}

Versus

Get-Service | Where-Object -FilterScript {$_.Status -eq 'Running' -and $_.Name -like 'Wi*'}

These two scripts return the same results, but one is more efficient. The first script follows the principle of filter left and immediately filters for services that start with 'Wi*', piping only the results that meet the criteria to the Where-Object command to then be filtered by the status. The second example returns all the running services and then pipes the full list of services to the Where-Object command, where the results are then filtered based on the status and name.

Here are results of the two commands.

Performance difference of filtering left versus not filtering left.

The difference between these two commands is almost a 65% increase in performance. While this is hardly noticeable on such a small-scale example, the increase in performance can make a significant difference on systems running numerous scripts against large datasets.

Filtered data is useful data

Once you learn the Where-Object cmdlet, you’ll never stop using it. Hopefully, this guide helps you on your path to PowerShell greatness.

If PowerShell isn’t your cup of tea, but you’d still love to get your hands on tons of useful filtered data, look no further than PDQ Inventory. PDQ Inventory automatically collects loads of information about your endpoints and makes that data easily accessible through a super simple user interface. Try it out for yourself with a 14-day free trial.

Brock Bingham candid headshot
Brock Bingham

Born in the '80s and raised by his NES, Brock quickly fell in love with everything tech. With over 15 years of IT experience, Brock now enjoys the life of luxury as a renowned tech blogger and receiver of many Dundie Awards. In his free time, Brock enjoys adventuring with his wife, kids, and dogs, while dreaming of retirement.

Related articles