Finding Text: Select-String
Select-String searches the text inside files for a word, in one file or in many at once, and shows each matching line with its file name and line number, as objects you can pick from like any other.
Apprentice · PowerShell · Operating System · Updated
After this lesson you can
- Search one file or many for a word with Select-String, and read each result's file name, line number and line
- Pick from Select-String's results by their properties, such as Filename and LineNumber, and list only the matching files with -List
Key takeaways
Select-Stringfinds every line that contains a word, in one file or, with a wildcard such aslogs\*, in many.- Each result reads file name, line number, then the line itself, split by colons.
- Capital letters don't matter to
Select-Stringunless you add-CaseSensitive. - The results are objects with properties such as
Filename,LineNumberandLine, and-Listkeeps only the first match in each file.
You know an email or a receipt somewhere mentions invoice 4471, but which of the six hundred files in your Downloads folder is it? Or a program has written a log of everything it did, ten thousand lines long, and you need the three lines that say "error". You could open each file and read, but that's slow, and tired eyes skip the very line you need. Even File Explorer's search, when it looks inside files at all, shows you the file and not the line. Select-String reads every line for you and shows only the ones you asked for. A help-desk tech searches a log this way, a web developer finds which file uses a name, and a data analyst finds which export mentions a customer.
A practice folder: the Archive's door logs
The town archive records every badge at every door: the time, the door, the badge number and what happened, one line each, such as 23:48 door=reading-room badge=4420 in. On Thursday morning the archivist found the reading-room lamp still on. Somebody was in there after closing, and four days of logs can say who. Copy these lines into PowerShell to make the logs and the staff list. Running them again is safe.
New-Item -ItemType Directory -Force -Path "$HOME\terminal-practice\archive\logs" | Out-Null
Set-Content -Path "$HOME\terminal-practice\archive\staff.txt" -Value 'BADGE NAME JOB', '2041 Imani Osei archivist', '3317 Rosa Lind cleaner', '4420 Nadia Petrov volunteer', '6188 Lee Park librarian'
Set-Content -Path "$HOME\terminal-practice\archive\logs\2026-09-14.log" -Value '09:02 door=west badge=6188 in', '14:20 door=map-room badge=2041 in (tour)', '15:05 door=map-room badge=2041 out (tour)', '17:31 door=west badge=6188 out', '19:15 door=east badge=3317 in', '20:40 door=east badge=3317 out'
Set-Content -Path "$HOME\terminal-practice\archive\logs\2026-09-15.log" -Value '09:10 door=west badge=6188 in', '10:30 door=reading-room badge=6188 in', '11:15 door=reading-room badge=6188 out', '17:45 door=west badge=6188 out', '19:22 door=east badge=3317 in', '20:51 door=east badge=3317 out'
Set-Content -Path "$HOME\terminal-practice\archive\logs\2026-09-16.log" -Value '09:05 door=west badge=6188 in', '13:40 door=front badge=4420 in', '17:40 door=west badge=6188 out', '19:18 door=east badge=3317 in', '20:45 door=east badge=3317 out', '23:48 door=reading-room badge=4420 in'
Set-Content -Path "$HOME\terminal-practice\archive\logs\2026-09-17.log" -Value '00:52 door=reading-room badge=4420 out', '00:58 door=front badge=4420 out', '09:00 door=west badge=6188 in', '14:10 door=map-room badge=2041 in (tour)', '14:55 door=map-room badge=2041 out (tour)', '17:35 door=west badge=6188 out'
They print nothing when they work.
Search one file: Select-String
The name reads like the others: select the lines of text that match. (Programmers call a piece of text a string.) Give it the word to look for first, then the file:
Set-Location "$HOME\terminal-practice\archive"
Select-String reading-room logs\2026-09-16.log
logs\2026-09-16.log:6:23:48 door=reading-room badge=4420 in
Read the result in three parts, split by the first two colons (:): the file, logs\2026-09-16.log; the line number, 6; then the whole line. Badge 4420 went into the reading room at 23:48 on Wednesday. In PowerShell 7 the word you searched for is highlighted on screen; Windows PowerShell 5.1 shows the same line without the highlight.
A file with no match prints nothing at all, with no error: Select-String reading-room logs\2026-09-14.log is a quiet "not in here". Now, whose badge is 4420? The staff list knows:
Select-String 4420 staff.txt
staff.txt:4:4420 Nadia Petrov volunteer
Search many files at once
The real power is searching every file in one go. Give Select-String a wildcard: logs\* (read "logs, backslash, star") means every file in the logs folder.
Select-String reading-room logs\*
logs\2026-09-15.log:2:10:30 door=reading-room badge=6188 in
logs\2026-09-15.log:3:11:15 door=reading-room badge=6188 out
logs\2026-09-16.log:6:23:48 door=reading-room badge=4420 in
logs\2026-09-17.log:1:00:52 door=reading-room badge=4420 out
Four lines from three files, in order. Nadia, the volunteer, went in late on Wednesday and left at 00:52 on Thursday: she'd been sorting the new donations and forgot the lamp. Two commands answered it, and they'd work the same on four hundred logs.
Capital letters don't matter to Select-String: READING-ROOM finds the same lines. That's called being case-insensitive, from the printers' "upper case" and "lower case". When capitals matter, say for Error but not error, add -CaseSensitive.
The word you search for is a pattern, and a few characters, such as ., $, ( and ), have special meanings in one. Patterns: Regular Expressions with -match teaches them. Until then, add -SimpleMatch when you search for text that holds them, such as a price: Select-String '$112.50' notes.txt -SimpleMatch.
On a Mac or Linux, write paths with / (logs/*). On Linux a file's name must also match its capitals, though the text inside still doesn't have to.
The results are objects
Each result is an object, the way each row of Get-ChildItem is, with properties you can pick with Select-Object:
Select-String reading-room logs\* | Select-Object Filename, LineNumber, Line
Filename LineNumber Line
-------- ---------- ----
2026-09-15.log 2 10:30 door=reading-room badge=6188 in
2026-09-15.log 3 11:15 door=reading-room badge=6188 out
2026-09-16.log 6 23:48 door=reading-room badge=4420 in
2026-09-17.log 1 00:52 door=reading-room badge=4420 out
Sometimes you want the files, not every line: which files mention the invoice? -List stops at the first match in each file, so each file appears once:
Select-String reading-room logs\* -List | Select-Object Filename
Filename
--------
2026-09-15.log
2026-09-16.log
2026-09-17.log
Because they're objects, the pipeline works on them like on any others: | Measure-Object counts them, as Counting and Grouping: Measure-Object and Group-Object showed.
Try it. Which days did someone take a tour of the map room? Search the logs for tour, then list only the files.
Try it in the terminal
Terminal Quest: The Archive, goals 1 to 4: played right on the lesson page.
The same archive's oldest treasure, the Star Map, has gone missing. Read the archivist's notice, search a month of door logs for the map room, write down whose badge it was, and count every log line with that badge.
Check yourself
2 questions to try on the lesson page.
So what
You don't have to read a file to find what's in it. Select-String turns "open everything and look" into one line, and it works the same on four files or four thousand.