2026-10-15 · powershell · ai engineering

The Windows side of the same story: Select-String and Invoke-RestMethod

PowerShell pipes objects, not text. For a code model that is a real advantage.

PS C:\Users\you> _

The PowerShell cmdlets worth knowing cold

Same idea as the Unix toolkit, one big difference: you filter on properties instead of parsing columns.

Select-Stringthe grep
Select-String "TODO" -Path src\* -Recurse
sls "api_key" . -Recurse -List
sls "error|warn" app.log
(sls "GET /" access.log).Count
sls "^#" config.ini -NotMatch
alias sls · -Recurse · -List filenames only · -NotMatch invert · -CaseSensitive · regex by default
Invoke-RestMethodthe curl
irm https://api.example.com/v1
(iwr https://example.com).Headers
iwr -Uri https://host/a.zip -OutFile a.zip
irm -Method Post -ContentType "application/json" `
  -Body '{"q":"hi"}' https://api.example.com
irm parses JSON into objects · iwr returns the raw response · -Method · -Headers @{} · -Body · -OutFile
Everyday toolkit
gci -Recurse -Filter *.logfind files (Get-ChildItem)
(gc f.txt) -replace 'old','new' | sc f.txtfind and replace in a file
… | Select-Object Name, Lengthpick the properties you want
gc app.log -Wait -Tail 20watch a log live
… | Group-Object | Sort Count -Desccount the most common values
(gc data.json | ConvertFrom-Json).items.idquery JSON, no jq needed
… | ForEach-Object { cmd $_ }run a command per item
… | Where-Object Status -eq "Running"filter on a property
Compare-Object (gc a.txt) (gc b.txt)see what changed
Get-Process node | Stop-Processfind and kill a process
Get-NetTCPConnection -LocalPort 3000who is holding a port
gci -Recurse | Measure-Object Length -Sumhow big is this folder
Compress-Archive dir\ out.zipzip a folder
Get-Command *-Service*discover cmdlets by name
cmd *>&1 | Tee-Object run.logcapture output and still see it
One pipeline to remember irm $url | ? message -match "error" | group level | sort Count -Desc | select -First 10 Fetch → filter → group → rank. Objects the whole way, so no column counting.
The cheat sheet. Everything below expands on it.

Why these two

Select-String (alias sls) is PowerShell's grep. Regex by default, recurses with -Recurse, inverts with -NotMatch. When an AI coding agent is working on a Windows box and needs to find every reference to a function, this is what it runs.

Invoke-RestMethod (alias irm) is PowerShell's curl — except it doesn't hand you a wall of JSON. It parses the response into a live object. irm $url then .items[0].id, and you're done. No jq required.

The twist: objects, not text

In Bash you pipe strings and parse columns with awk. In PowerShell you pipe [FileInfo] or [Process] objects and filter on properties: Where-Object Status -eq "Running". For a code model that is a real advantage — it reasons about fields by name instead of guessing which column holds what.

Different philosophy, same lesson: small tools with clear contracts are the interface language models were waiting for.


The full reference

PowerShell first, then the Bash equivalents so you can translate in either direction.

Select-String

commandwhat it does
sls "pattern" -Path src\* -Recurserecursive search; output shows file:line:text
sls "pattern" . -Recurse -Listfirst match per file only — filenames, effectively
sls "pattern" file -CaseSensitivecase-sensitive (default is insensitive)
sls "pattern" file -SimpleMatchliteral string, no regex
sls "pattern" file -NotMatchinvert
sls "pattern" file -Context 1,31 line before, 3 after
(sls "pattern" file).Countcount matching lines
sls "pattern" file | select -Expand Linejust the matching text
(sls "(\d+)" file).Matches.Groups[1].Valuecapture-group extraction
gci -Recurse -Include *.py | sls "def "restrict by file type via the pipeline

Invoke-RestMethod and Invoke-WebRequest

commandwhat it does
irm $urlGET, JSON auto-parsed into objects
(iwr $url).StatusCodestatus code (iwr = raw response object)
(iwr $url -Method Head).Headersheaders only
iwr $url -OutFile a.zipdownload to a file
irm $url -Method Post -Body ($obj | ConvertTo-Json) -ContentType "application/json"JSON POST from a PowerShell object
irm $url -Headers @{Authorization="Bearer $t"}auth header
irm $url -Credential (Get-Credential)prompted basic auth
irm $url -Form @{file=Get-Item report.pdf}multipart upload (PS 7+)
irm $url -MaximumRetryCount 3 -TimeoutSec 10retries and timeout (PS 7+)
irm $url -SkipHttpErrorCheckdon't throw on 4xx/5xx; inspect instead (PS 7+)
iwr $url -Verbosesee the request/response details

Files and folders

commandwhat it does
gci -Recurse -Filter *.logfind by name (Get-ChildItem; alias ls, dir)
gci -Recurse | ? LastWriteTime -gt (Get-Date).AddDays(-1)modified in the last day
gci -Recurse | ? Length -gt 100MBlarger than 100 MB
gci *.tmp -Recurse | Remove-Item -WhatIfpreview a delete; drop -WhatIf to do it
gc file -Head 20 / gc file -Tail 20first / last 20 lines (Get-Content)
gc app.log -Wait -Tail 20follow a growing file
(gc file).Countline count
gc a, b | sc cconcatenate (Set-Content)
Test-Path filedoes it exist
Resolve-Path .\xabsolute path

Transforming data

commandwhat it does
(gc f) -replace 'old','new' | sc freplace in a file (regex)
gc f | select -Skip 9 -First 11lines 10–20
… | % { ($_ -split '\s+')[0] }first whitespace column
Import-Csv f.csv | select col3CSV column by header name
Import-Csv f.csv | ? { [int]$_.qty -gt 100 }rows where a column exceeds 100
… | sort col2 | Export-Csv out.csv -NoTypeInformationsort and write CSV
"abc".ToUpper()string methods are right there
… | group | sort Count -Descfrequency count (Group-Object)
gc d.json | ConvertFrom-Json | % items | select id, namereshape JSON
$obj | ConvertTo-Json -Depth 5object back to JSON
… | % { cmd $_ }run cmd once per item (ForEach-Object)
… | Tee-Object out.logwrite to file and still print
… | Format-Table -Auto / Format-Listrender as table / list (display only — keep it last)

Processes, ports, disk, services

commandwhat it does
Get-Process nodefind a process (alias ps)
Get-Process | ? CommandLine -match "app.py"match on full command line (PS 7+)
Stop-Process -Id PID -Forcekill
Get-NetTCPConnection -LocalPort 3000what's on port 3000 (Windows)
Get-NetTCPConnection -State Listen | select LocalPort, OwningProcessall listeners with PIDs
Get-Service | ? Status -eq Runningrunning services
Restart-Service namerestart one
Get-PSDrive -PSProvider FileSystemdisk free per drive
gci | % { "{0,-30} {1,10:N1} MB" -f $_.Name, ((gci $_ -Recurse -File | measure Length -Sum).Sum/1MB) }size of each folder here
Start-Job { cmd } / Receive-Jobrun in the background
cmd *>&1 | Tee-Object run.logcapture every stream together

Archives, help, history

commandwhat it does
Compress-Archive dir\ out.zipzip a folder
Expand-Archive in.zip -DestinationPath out\unzip
Get-Command *-Service*discover cmdlets by name
Get-Help cmd -Examplesworked examples for any cmdlet
cmd | Get-Memberwhat properties and methods does this object have — the single most useful habit
Get-Alias slswhat an alias expands to
Get-History | ? CommandLine -match sshfind that command you ran last week
ctrl-rreverse history search (PSReadLine)
$PSVersionTablewhich PowerShell am I on

Bash ↔ PowerShell translation table

bashpowershell
grep -rn x .sls x . -Recurse
curl -s url | jq .a(irm url).a
find . -name "*.log"gci -Recurse -Filter *.log
sed -i 's/a/b/g' f(gc f) -replace 'a','b' | sc f
awk '{print $1}'% { ($_ -split '\s+')[0] }
tail -f fgc f -Wait -Tail 10
sort | uniq -c | sort -rngroup | sort Count -Desc
xargs -n1 cmd% { cmd $_ }
ps aux | grep xGet-Process x
lsof -i :3000Get-NetTCPConnection -LocalPort 3000
du -sh *gci | measure Length -Sum (per folder, see above)
tar -czf o.tgz d/Compress-Archive d\ o.zip
chmod +x s.sh(not needed — execution policy instead: Set-ExecutionPolicy RemoteSigned)
which cmdGet-Command cmd
man cmdGet-Help cmd
2>&1 | tee f*>&1 | Tee-Object f
Portability note. Get-NetTCPConnection, Get-Service, and Get-PSDrive details are Windows-specific. On PowerShell 7 for macOS/Linux, the file, text, JSON, and web cmdlets all work the same; for ports and services, fall back to the native tools (lsof, systemctl).