Filtering: Where-Object and Comparisons

Where-Object keeps only the objects that pass a test, such as every file over 1 MB or every song, and the test is written with comparisons read aloud as words, -eq "equals", -gt "greater than", -like "is like", with $_ standing for this item.

Apprentice · PowerShell · Operating System · Updated

After this lesson you can

  • Keep only the objects you want with Where-Object, writing the test with $_ for this item
  • Compare with -eq, -ne, -gt, -lt and -like, and say each one aloud

Key takeaways

  • Where-Object lets through only the objects that pass a test, like a sieve. Where-Object Length -gt 1MB keeps every file over 1 MB.
  • Comparisons are words after a dash: -eq equals, -ne not equals, -gt greater than, -lt less than, and -like is like, with wildcards.
  • $_ (read "this item") is the object being tested. In braces you can write { $_.Length -gt 1MB -and $_.Extension -eq '.mp4' }.
  • Text comparisons ignore capital letters. > is not greater than: it means "into a file".

Your phone says "Storage almost full", or a cloud backup refuses a file because it's too big. The first question is always the same: which files are over some size? File Explorer can sort a folder by size, but it can't answer "every file over 1 MB, in every folder, and nothing else". People sift lists like this all day: a teacher picks out the late assignments, a shop owner the orders over $100, an IT tech the programs using the most memory.

This lesson uses the drive folder from The Pipeline: |. If you don't have it yet, run that lesson's practice-drive lines first, then Set-Location ~\terminal-practice\drive.

Keep what passes a test: Where-Object

A kitchen sieve lets the water through and keeps the pasta. Where-Object is a sieve for objects: it tests each one that arrives through the pipe and lets through only those that pass. You name a property, a comparison and a value:

Get-ChildItem -Recurse -File | Where-Object Length -gt 1MB | Select-Object Name, Length
Name                     Length
----                     ------
game-setup.exe          2355200
phone-backup.zip        1945600
unfinished-download.tmp 1126400
rain-sounds.mp3         1331200
road-trip-mix.mp3       1740800
birthday-party.mp4      1536000
graduation.mp4          2867200

Read it aloud: "every file, where Length is greater than one megabyte." Seven of the eighteen files pass. A file's Length is counted in bytes, the smallest unit of a file's size; 1MB is PowerShell's way of writing 1,048,576 of them, so you never count zeros. 1KB and 1GB work the same way. The Select-Object at the end only keeps the table narrow.

The comparisons, read aloud

Each comparison is a word after a dash, short for what you'd say:

Comparison Read it Example
-eq equals Where-Object Extension -eq '.mp3'
-ne not equals Where-Object Extension -ne '.tmp'
-gt greater than Where-Object Length -gt 1MB
-lt less than Where-Object Length -lt 100KB
-like is like (with wildcards) Where-Object Name -like '*birthday*'

-ge ("greater than or equal") and -le ("less than or equal") are there too. -like takes the wildcards from Wildcards and Filters, so '*birthday*' means "birthday anywhere in the name":

Get-ChildItem -Recurse -File | Where-Object Name -like '*birthday*' | Select-Object Name, Length
Name                Length
----                ------
birthday-cake.png   532480
birthday-party.mp4 1536000

Text comparisons ignore capital letters, so Where-Object Extension -eq '.MP3' finds the three songs too. To make capitals count, put a c in front: -ceq, -clike.

$_: this item

For a test with two parts, write it in braces. The braces hold a script block, a small piece of code that Where-Object runs once for each object. Inside it, $_ (read "this item", or "dollar underscore") is the object being tested right now:

Get-ChildItem -Recurse -File | Where-Object { $_.Length -gt 1MB -and $_.Extension -eq '.mp4' } | Select-Object Name, Length
Name                Length
----                ------
birthday-party.mp4 1536000
graduation.mp4     2867200

Read it aloud: "where this item's Length is greater than one megabyte, and this item's Extension equals .mp4." -and needs both parts to be true; -or needs one of them. $_.Length is the dot from Properties and Methods. $PSItem is a longer name for $_, and where and ? are short names for Where-Object.

The one mistake everybody makes: writing > for "greater than". In PowerShell > means "into a file", as in Writing to Files: Set-Content, Add-Content and >. Where-Object { $_.Length > 1MB } lets nothing through, and leaves a new file in your folder named 1048576. Use -gt. (If you made that file, Remove-Item 1048576 takes it away.)

Running programs, and filtering early

The same sieve works on anything. Get-Process | Where-Object WorkingSet64 -gt 200MB lists every running program using more than 200 MB of memory (WorkingSet64 is how much memory a program is using right now). Try it: the list is your own computer's, so no two people see the same one.

When you only need files by name, Get-ChildItem -Filter, from Wildcards and Filters, is faster, because Windows does the sifting before PowerShell sees a file. Where-Object is for everything else: size, date, anything an object knows.

Windows PowerShell 5.1 runs all of this the same way. On a Mac or Linux, -eq still ignores capital letters, but file names themselves care about them.

Try it. List every file on the drive smaller than 100 KB. Then every photo: .jpg or .png, written with -or.

Check yourself

2 questions to try on the lesson page.

So what

Where-Object turns "find me the ones that…" into one line: name the fact, the comparison and the value, and only what passes gets through.

Key terms

Cheat sheets

Related