The key to making the most of Windows PowerShell is to understand how objects and the pipeline work together to let you retrieve exactly the information you need. Each command in the pipeline generates one or more objects and passes them down the pipeline to the next command. Integral to this process is the ability to filter the objects and their data as they pass from one command to the next. To that end, PowerShell includes a number of cmdlets that, in one way or another, let you create commands that refine your pipeline's output. I'll discuss five of those cmdlets—Where-Object, Select-Object, Select-String, ForEach-Object, and Out-GridView—and provide examples that demonstrate the many ways in which you can filter the pipeline objects and their data.

Related Articles:
PowerShell Basics: Introduction to Objects
PowerShell Basics: Select-Object

The Where-Object Cmdlet

Where-Object is one of the most commonly used cmdlets in PowerShell and probably the one most often used for filtering data. This cmdlet filters objects passed down the pipeline based on the search criteria you specify in a script block included with the cmdlet. In other words, the script block determines which objects are permitted to continue down the pipeline and which are not.

For example, suppose you want to use the Get-Command cmdlet to retrieve only the available cmdlets and no other types of commands. To do so, you can pass the Get-Command objects down the pipeline to a Where-Object command:

Get-Command | Where-Object {$_.CommandType -eq 'cmdlet'}

As you can see, you specify the Where-Object cmdlet, followed by the script block, which is enclosed in curly brackets. The script itself is made up of three components: the property on which to filter the data (CommandType), a comparison operator (-eq), and the value on which to base the filter (cmdlet).

When calling a property in a Where-Object script block, you must precede the property name with the $_ symbol, followed by a period. The $_ symbol represents the current object in the pipeline. Because the Where-Object command is applied to every object as it passes down the pipeline, the $_ symbol makes it possible to associate the specified property with each object, without having to know the number of objects being passed down the pipeline and without needing to differentiate between those objects.

The next element within the script block is the comparison operator, which is -eq (equal to) in this case. PowerShell uses characters for its comparison operators, unlike many languages, which use arithmetic symbols such as an equals sign (=) or greater than symbol (>). In addition to the -eq operator, PowerShell supports comparison operators such as:

  • -ne (not equal to)
  • -lt (less than)
  • -le (less than or equal to)
  • -gt (greater than)
  • -ge (greater than or equal to)
  • -like (like—a wildcard comparison)
  • -notlike (not like—a wildcard comparison)
  • -contains (contains the specified value)
  • -notcontains (doesn't contain the specified value)

Although this isn't a complete list, it covers many of the basics. Be sure to check the PowerShell documentation for details about the comparison operators that PowerShell supports. Also note that PowerShell 3.0 introduced a number of new operators but continues to support the original operators.

Following the comparison operator is the value to be compared. In this case, the value is cmdlet. Because it's a string, you must enclose it in either single or double quotes. The difference between the two options is in how variables and special characters are handled. Again, check the PowerShell documentation for details about working with string values.

When taken as a whole, the expression within the script block is saying that, for an object to be included in the final results, the value of the CommandType property must equal cmdlet. If the values are equal, the script block evaluates to True and the object is retained; otherwise, it's filtered out from the other objects.

Figure 1 shows a partial list of the cmdlets returned. Notice that the CommandType column includes only the value Cmdlet, indicating that the results include only that type of command. If you don't include the Where-Object command, the list would include other types of commands, such as functions and aliases.

Filtering Get-Command's Output So That Only the Cmdlets Are Returned

Once you understand the basics of how to build your script block, you can switch things around as necessary. For example, the following statement uses the -ne (not equal to) comparison operator to compare the property value with the cmdlet value:

Get-Command | Where-Object {$_.CommandType -ne 'cmdlet'}

This time around, the results are quite different and include everything but cmdlets. Figure 2 shows a partial list of the type of commands you'd now receive.

Filtering Get-Command's Output So That Everything Except the Cmdlets Are Returned

You're also not limited to a particular property in the script block expression. For example, you can specify the Name property and the -like comparison operator:

Get-Command | Where-Object {$_.Name -like '*clear*'}

Notice that the value specified after the -like comparison operator includes the asterisk (*) wildcard at the beginning and end. The -like comparison operator lets you use wildcards within the specified value to help with the search. As a result, this statement will return all commands that include the word clear anywhere in the name, as shown in Figure 3. In this case, the returned commands all begin with the word clear, but the word could be included in any part of the name.

Filtering Get-Command's Output So That All Commands That Include the Word

The Where-Object script block also lets you use logical operators to link together multiple expressions. This way, you can evaluate multiple conditions within one script block. The logical operator determines how you treat the individual expressions in order to arrive at a final evaluation. If the script block as a whole evaluates to True, the object is included in the results. PowerShell supports several logical operators:

  • -and (The script block evaluates to True if the expressions on both sides of the logical operator evaluate to True.)
  • -or (The script block evaluates to True when one of the expressions on either side of the logical operator evaluates to True.)
  • -xor (The script block evaluates to True when one of the expressions on either side of the logical operator evaluates to True and the other expression evaluates to False.)
  • -not or ! (Negates, or reverses, the script element that follows it.)

Let's look at a Where-Object script block that joins two expressions to get a better sense of how this works. The following script block uses the -and logical operator to join the expressions:

Get-Command | Where-Object {($_.Name -like '*clear*') `
-and ($_.CommandType -eq 'cmdlet')}

The first expression specifies that the Name property value must contain the word clear. The second expression specifies that the CommandType property value must be cmdlet. Because the -and logical operator is used, both these conditions must evaluate to True for the script block as a whole to evaluate to True. As you can see in Figure 4, only those commands that meet both conditions are included.

Filtering Get-Command's Output So That Only Cmdlets That Include the Word

Now let's see what happens when you throw the -not logical operator (which can also be represented with !) into the mix:

Get-Command | Where-Object {($_.Name -like '*clear*') `
-and !($_.CommandType -eq 'cmdlet')}

Because you're negating the second condition, you're essentially saying that the Name property must include the word clear, but the CommandType property can't be a cmdlet. As a result, the statement now returns only a single function, as Figure 5 shows.

Filtering Get-Command's Output So That Any Commands That Include the Word

As handy as the Where-Object cmdlet is, one thing that's important to point out is that many cmdlets support parameters that filter objects for you. For example, the Get-Command cmdlet supports the -CommandType parameter. By using these types of parameters, you're passing less data down the pipeline initially, which can help with performance when returning large data sets. The following statement shows how to make use of this parameter:

Get-Command -CommandType cmdlet |
Where-Object {$_.Name -like '*clear*'}

As you can see, when you call the Get-Command cmdlet, you include the -CommandType parameter and the name of the command type (in this case, cmdlet). You then pipe your smaller data set to the Where-Object command, where you filter out all objects except those whose Name property contains clear, as shown in Figure 6.

Using Get-Command's -CommandType Parameter to Filter Objects

Some cmdlets actually support a -Filter property. For instance, the Get-ChildItem supports such a property to further qualify the -Path property:

Get-ChildItem -Path C:\DataFiles -Filter *txt |
Where-Object {$_.Length -ge 1000}

In this case, the -Filter parameter is used to specify that only .txt files be returned. Notice that you can use the asterisk wildcard to indicate that you want all files ending in txt. Figure 7 shows an example of what the results might look like.

Using Get-ChildItem's -Filter Property

Now let's look at another aspect of the Where-Object cmdlet. Starting with PowerShell 3.0, you can use an alternative approach to specifying the command's logic. For example, you can pipe the Get-Service cmdlet's output to the Where-Object cmdlet and specify that the Status property value must equal the value running:

Get-Service |
Where-Object -Property Status -eq -Value 'running'

Notice that you're no longer including the script block. Instead, you're simply specifying the property, followed by the comparison operator, and finally the value to use for comparison. Figure 8 shows a partial list of running services returned.

Filtering Get-Service's Output So That Only Running Services Are Returned

You can shorten this last statement even further. Instead of spelling out Where-Object, you can simply use thewhere alias, which is supported in all versions of PowerShell. In addition, you don't need to specify the -Property or -Value property names. If PowerShell expects a parameter in a specific order and you supply the parameter in that order, you can sometimes omit the parameter name. However, there are no hard and fast rules about when you can or can't omit the parameter name, so you should see the PowerShell Help files for details about a specific cmdlet. In the case of the Where-Object cmdlet, you can pare the statement down to:

Get-Service | where Status -eq 'running'

This statement returns the same results as the previous statement. The following statement also returns the same results:

Get-Service | where {$_.Status -eq 'running'}

As you can see, this statement is once again using the script block format but with the where alias.

Let's look at one more trick you can perform with the Where-Object cmdlet. The following Where-Object statement specifies only the property in the script block, with no comparison operator or target value:

Get-Service | where {$_.DependentServices}

When you include only the property in a script block, PowerShell returns an object only if the specified property contains a value. In this case, you're returning those local services whose DependentServices property contains a value—in other words, those services with dependent services. Figure 9 shows a sample of the services returned.

Returning Those Services with Dependent Services

You can achieve the same results in PowerShell 3.0 by using the shortened version of the Where-Object cmdlet:

Get-Service | where DependentServices

You can verify that the statement is returning the desired information by looking more closely at one of the services returned. For example, the following statement retrieves data for the eventlog service, which is one of the services listed in Figure 9:

Get-Service eventlog | Format-List

In this case, you're piping the Get-Service results to the Format-List cmdlet so that you can view more details about the returned object. As you can see in Figure 10, the eventlog service has two dependent services: Wecsvs and Schedule.

Returning the Services Dependent on the eventlog Service

I've spent a lot of time discussing the Where-Object cmdlet because it plays such an important role in filtering data. However, it's not the only method available for refining results, so let's move on to the next one.

The Select-Object Cmdlet

There might be times when you need to refine your results a bit differently from what the Where-Object cmdlet provides. That's where the Select-Object cmdlet comes in. Using this cmdlet, you can filter your results based on property names, number of objects, or other criteria. For example, the following statement pipes the Get-Process cmdlet's output to the Select-Object cmdlet and specifies that only the ProcessName and CPU properties be returned:

Get-Process | Select-Object -Property ProcessName, CPU |

As you can see, after you add the Select-Object cmdlet, you specify the -Property parameter, followed by the property names (separated by a comma). You then pipe the results to the Format-List cmdlet to make them easier to read. Figure 11 shows a partial list of the processes returned by this statement. Not surprisingly, the results include only the ProcessName and CPU properties, as specified.

Filtering Get-Process's Output to Return the ProcessName and CPU Properties for All Processes

You can shorten this Select-Object command by using the select alias in place of the Select-Object name and by dropping the -Property parameter name:

Get-Process | select ProcessName, CPU | Format-List

This statement returns the same results as the preceding one, only this command is a bit simpler to type. Even so, it's the results that are important, and in this case, even more so. Unlike the Where-Object cmdlet, which simply filters out objects, the Select-Object cmdlet actually generates a different type of object. To verify this, you can use the Get-Member cmdlet to retrieve details about the object being returned by the cmdlet. The following statement is piping the Select-Object cmdlet's output to the Get-Member cmdlet:

Get-Process | select ProcessName, CPU | gm

In this case, the gm alias has been used for the Get-Member cmdlet.

Figure 12 shows the results. What might strike you immediately is the relatively small number of members, with the CPU and ProcessName properties being two of them. Also notice that the object returned by Select-Object is Selected.System.Diagnostics.Process. However, the object returned by Get-Process is actually System.Diagnostics.Process, and it includes a lot more members. The Select-Object cmdlet helps reduce your load as you pass data down the pipeline. But that means you can't reference one of the discarded members down the road. You're stuck with what you got here.

Retrieving Details About the Object Being Returned by Select-Object

For instance, in the previous examples in this section, you used the Select-Object cmdlet to return only the ProcessName and CPU properties. Suppose you now pipe the Select-Object command to a Where-Object command that filters on the Handles property:

Get-Process | select ProcessName, CPU |
where Handles -gt 500 | Format-List

This statement will return no results because that property is no longer in the pipeline, even though it's one of the output properties of the Get-Process command. However, you can address this issue by simply reversing the order of the Where-Object and Select-Object commands:

Get-Process | where Handles -gt 500 |
select ProcessName, CPU | Format-List

If you're going to pipe your Select-Object command output to a Where-Object command (or a similar command), you can reference only properties that still exist. For example, let's use the Select-Object cmdlet to return only the ProcessName and CPU properties, then use the Where-Object cmdlet to return only those processes whose CPU usage is greater than 10:

Get-Process | select ProcessName, CPU |
where CPU -gt 10 | Format-List

In this statement, you're piping the Get-Process results to the Select-Object command, then piping those results to the Where-Object command, ending with the Format-List cmdlet to make it easier to read. Your results should look similar to those in Figure 13.

Returning Only Those Processes Whose CPU Usage Is Greater Than 10

Now let's look at another aspect of the Select-Object cmdlet worth noting. This cmdlet makes working with sorted data easy because you can select a specific number of objects from the data being passed down the pipeline. For example, you can use the Sort-Object cmdlet to sort data by the CPU property (in descending order), then use the Select-Object cmdlet to return the first five processes:

Get-Process | sort CPU -Descending | select -First 5

After you sort the data, you can send it to the Select-Object command and specify the number of rows you want to return. If you run this statement on your system, you might see results similar to those shown in Figure 14.

Returning the Top Five CPU-Consuming Processes

Now let's try something else with the Select-Object cmdlet. Let's create a variable ($file) to hold a file object, then pipe the variable to a Select-Object command:

$file = Get-Item C:\DataFiles\ErrorOutput.txt
$file | select *

In the first statement, you're using the Get-Item cmdlet to return information about the ErrorOutput.txt file, then assigning that information to the $file variable. In the second statement, you're piping the variable to the Select-Object cmdlet. The only argument you need to specify is the asterisk wildcard, which indicates that all properties should be returned. As you can see in Figure 15, by using this method to retrieve data, you can return far more information about an object than you would otherwise. If you had instead piped the variable to the Format-List cmdlet, you would have received only about half this information.

Retrieving a Lot of Information About the ErrorOutput.txt File

Let's look at one more feature of the Select-Object cmdlet. As I mentioned previously, when filtering out data, Select-Object returns an object whose type is different from what had been passed to the command in the pipeline. One of the advantages of this is that you can add a custom property as you pass the object along the pipeline. For example, suppose you want to add a new property named LengthKB that converts bytes to kilobytes so that the file size is consistent with what you typically see in Windows Explorer. To do so, you add the LengthKB property to your object and set its value to equal the Length property divided by 1024:

$file | select Name, @{Name='LengthKB'; `
Expression={$_.Length/1024}} | Format-List

To add the property to your object, you specify the @ symbol, followed by a script block. Within the script block, you assign a name to the property and define the expression, which is enclosed in its own set of curly brackets. As the following results show, the statement returns two properties—Name and LengthKB—with the file size in kilobytes:

Name : ErrorOutput.txt
LengthKB : 3074.3525390625

As you've seen, the Select-Object cmdlet is a handy tool for filtering your results in a variety of ways. And it can do even more than what I've shown you here.

The Select-String Cmdlet

The Select-String cmdlet lets you move into new territory: You can use it to search an input string or a file's contents for a specific value. This cmdlet searches for text or text patterns within the target data and returns matching content. It operates on a line-by-line basis, filtering out all lines that don't contain the specified string and returning those that do. In addition, the cmdlet lets you specify that nonmatching lines are returned instead of matching lines. You can even test for the existence of matches, without returning the actual data.

As with any PowerShell cmdlet, the best way to understand how Select-String works is to see it in action. Suppose that you want to search the text files in the DataFiles directory to determine whether any of them contain the value error output. You can use the Select-String cmdlet in a statement like this:

Select-String -Path C:\DataFiles\*.txt `
-Pattern 'error output'

First, you use the cmdlet's -Path parameter to specify the location of the files. Notice that you can use wildcards in that parameter's value. Then, you use the -Pattern parameter to specify the string for which you're searching. In this case, the command returns only one line from one file:

DataFiles\ErrorOutput.txt:1:--Error Output

The information returned shows the folder and filename (DataFiles\ErrorOutput.txt), followed by the line number (1), and the actual line of text (--Error Output). Colons separate each entry. If additional lines in this file had contained the target string, those lines would've been returned as well. In addition, any matching lines in any other text files within the target folder would've been returned.

Like other cmdlets, the Select-String cmdlet lets you shorten your command. You can use the sls alias for the cmdlet name. You can also drop the -Pattern parameter name. The following statement returns the same results as the preceding one but does so with fewer keystrokes:

sls -Path C:\DataFiles\*.txt 'error output'

As I mentioned previously, Select-String returns the folder and filename, line number, and line of text by default. However, there might be times when you don't want to return all that information. As with any other command, you can pipe your results to the Select-Object cmdlet to perform additional filtering:

sls -Path C:\DataFiles\*.txt 'error output' |
select LineNumber | Format-List

This time around, the results are piped to a Select-Object command that filters out all the properties except LineNumber. The output is then piped to the Format-List cmdlet, which returns the results:

LineNumber : 1

If multiple files had contained the specified value, you would have wanted more information, but you get the idea.

The Select-String cmdlet lets you conduct case-sensitive searches. You need only append your command with the -CaseSensitive parameter:

sls -Path C:\DataFiles\*.txt 'error output' -CaseSensitive

Not surprisingly, the command no longer returns any results because the value for which you're searching is capitalized within the file. However, you can modify the value in the command:

sls -Path C:\DataFiles\*.txt 'Error Output' -CaseSensitive

The command now returns the results you'd expect:

DataFiles\ErrorOutput.txt:1:-- Error Output

Another important parameter that the Select-String cmdlet supports is -Quiet, which tells the cmdlet to return only the value True if at least one line contains the specified value:

sls -Path C:\DataFiles\*.txt 'Error Output' `
-CaseSensitive -Quiet

As you can see, you just need to add the -Quiet parameter. Now the command will return only the value of True, as long as there's at least one match. Note that it'll return only one instance of True, even if multiple lines contain the matching value. Admittedly, receiving a value of True doesn't tell you much, but at least you know you're looking in the correct place.

The -Quiet parameter can be handy if your command will return many lines. Although that's not the case in these examples, it would be if you modified the command as follows:

sls -Path C:\DataFiles\*.txt 'Error Output' -Quiet -NotMatch

Notice that the -CaseSenstive parameter has been replaced with -NotMatch, which reverses the logic passed into the command. As a result, all the lines that don't contain the value Error Output will be returned, which in this case would be many lines. In other words, almost every line in every file would be returned, except the one line in the one file that you know contains the specified value.

The Select-String cmdlet has another parameter, -Context, with which you should be familiar. This parameter lets you specify the number of lines to be returned before and after the line that contains the matching value. This can provide the context needed to better understand the returned data in those situations when a single line isn't enough to tell you what's really going on.

For example, you can use the -Context parameter to specify that you want the results to include the two lines before and after the target line:

sls -Path C:\DataFiles\*.txt 'message C' -Context 2

In this case, you're searching the target files for the text message C. Notice that the -Context parameter has been appended to the command, along with the value 2.

As it turns out, only one line in one file contains the text message C, as Figure 16 shows. This line is highlighted with the greater than symbol at the beginning of the row. As instructed, the two rows before and two rows after this line are also included.

Returning the Two Lines Before and After the Target Line

The ForEach-Object Cmdlet

In a discussion about filtering, I'd be remiss if I didn't mention the ForEach-Object cmdlet. This cmdlet iterates through the objects passed down the pipeline and performs a specified operation on each one. In some cases, this operation results in an object being filtered out of the pipeline.

But first, let's start with some basics to better understand how the ForEach-Object cmdlet works. Suppose you want use the Get-Service cmdlet to retrieve those services whose name begins with sql. You can pipe these results to a ForEach-Object command in order to return only one property:

Get-Service sql* | ForEach-Object -Process {$_.DisplayName}

All you've done here is pipe the objects to the ForEach-Object command, which includes the -Process parameter and a script block passed in as the parameter value. The script block itself uses the $_ symbol to reference the current object and the DisplayName property in order to return the value associated with that property. Figure 17 shows the services you might see on a system if you were to run this command.

Retrieving Those Services Whose Name Begins with sql

The ForEach-Object cmdlet supports an alias (foreach) and lets you drop the -Process parameter name. The following statement returns the same results as the preceding one:

Get-Service sql* | foreach {$_.DisplayName}

If you want to return a second property, you can simply add it to your script block, separated by a semi-colon:

Get-Service sql* | foreach {$_.DisplayName; $_.Status}

As you can see in Figure 18, the statement now returns the Status property values as well as the DisplayName values.

Retrieving the Status of the Services Whose Name Begins with sql

To make the results more readable, you can add an empty string to the script block:

Get-Service sql* | foreach {$_.DisplayName; $_.Status; ""}

The empty string adds an empty line after each status in the results. As Figure 19 shows, these empty lines make it easier to see which services are stopped and which services are running.

Inserting Empty Lines to Make It Easier to See the Status of Each Service

But what you've seen so far is nothing new. You can easily achieve similar results by using the Select-Object cmdlet. However, the difference lies in the script block itself, which lets you write very elaborate expressions for how each object is processed. Let's look at another example to give you a better sense of what you can do with a ForEach-Object script block. In the following command, the script block includes an if statement that returns the display name of each service that's running:

Get-Service sql* |
foreach {if ($_.Status -eq 'running') {$_.DisplayName}}

Within the script block, you specify the if keyword and the condition that must evaluate to True. In this case, the Status property value must equal running. If that condition evaluates to True, the remaining part of the if construction runs, which is to merely call the DisplayName property. As the following results show, only two services meet all the criteria:

SQL Server Browser
SQL Server VSS Writer

Although this is another fairly simple example, it demonstrates some of what you can do with the ForEach-Object cmdlet. However, just to make sure you appreciate the cmdlet's value, let's look at another example. Suppose you want to create new files that contain the content of some old files. To do so, you can use the statements:

$content = Get-Content C:\DataFiles\TestFolder\*.txt
$content | foreach {Out-File ($_.PSPath + '.csv') `
-Append -InputObject $_.ToString()}

The first statement retrieves the content from a set of text files in the C:\DataFiles\TestFolder folder and assigns the content to the $content variable. The second statement pipes that variable to a ForEach-Object command that includes a script block. This script block will create the new files that will contain the contents of the old files.

You begin the script block by using the Out-File cmdlet to create the file. As part of the process, you use each object's PSPath property to retrieve the current path and filename in order to add the .csv extension to that name. For example, one of the files in the folder is test1.txt file. A new file will be created to correspond with that file and will be named test1.txt.csv.

So far, all you've done is create the new files with the new filenames. You must now copy the content from each old file to its related new one. For that, you add the -Append parameter, followed by the
-InputObject parameter, which lets you grab the data from the old file (the current object), convert it to a string, and add it to the new file. In this case, you'll end up with five new text files that contain the content of the five original text files.

No doubt, if you were going to do something like this, you might come up with a more elaborate file scheme and perhaps even move the files to a different folder. You could even include additional logic. For instance, you could add an if statement that tests whether an existing file contains data before copying it to a new file, as shown here:

$content |
foreach {if ($_.Length -gt 0) {Out-File `
($_.PSPath + '.csv') -Append -InputObject $_.ToString()}}

As you can see, the script block has been modified to include an if statement that specifies that the Length property must have a value greater than 0 for a file to be copied. As this example demonstrates, the ForEach-Object cmdlet can be valuable not only for filtering data, but also for performing numerous other tasks at the same time.

The Out-GridView Cmdlet

The last PowerShell cmdlet related to filtering that I want to demonstrate is the Out-GridView cmdlet. This cmdlet sends your output to a new window that displays your data in a grid. You can then filter the returned data however you want, without having to generate additional PowerShell commands.

To open the grid window, you simply pipe your results to the Out-GridView cmdlet, like this:

Get-Command -CommandType cmdlet | Out-GridView

The statement will launch the grid window and display your results, as shown in Figure 20. Notice that the window shows the initial list of available cmdlets. To see the rest of them, simply scroll down. Every cmdlet available in your version of PowerShell should be listed in the window.

Using the Out-GridView Cmdlet to Launch the Display Grid

Notice that the grid window has a Filter text box at the top. You can filter the results by simply entering a value. For example, in Figure 21, the value get-c is entered. Every cmdlet name that contains that value, exactly as specified, is displayed in the grid. If a cmdlet's CommandType or ModuleName property contains the specified value, that cmdlet would also be displayed in the grid. The value can appear in any part of the name, as long as it appears exactly as specified.

Filtering Your Results as You Type

However, the grid window lets you be even more precise with your searches. If you click the Add criteriabutton, you can add specific properties, which you can then search. For example, in Figure 22, you'll see that the Name property has been added as a search criterion and the value alias has been specified. Now the cmdlets listed are only those in which the word alias appears somewhere in the name.

Searching for Cmdlets That Contain a Specific Word

Certainly, the Out-GridView cmdlet can be a handy tool when you need to search through a lot of data quickly and easily or when you need to filter data in different ways. As long as you keep the grid window open, all the original data is available for searching and filtering. However, beyond that, there's not much else you can do with the data in the grid window. Still, in certain situations, it's an effective way to filter your results quickly and easily.

PowerShell and Its Filters

There are many ways you can filter objects and their data in PowerShell. For example, you can use the Where-Object, Select-Object, Select-String, ForEach-Object, and Out-GridView cmdlets either separately or in conjunction with each other.

Now that you've had an overview of the various ways you can filter data, you should be good to go. But keep in mind that what I've shown you represents only some of what you can do with each of these cmdlets, so be sure to check the PowerShell documentation for further information about each one. You'll find plenty there to keep you in filtering heaven.