select-object — Pick specific properties or first/last N objects across all 5 shells
Equivalents in every shell
command | head -n 10For picking the first N records, `head -n 10`. For picking columns out of structured text, `cut -f1,3` (tab-delimited) or `awk '{print $1, $3}'` (whitespace). bash has no single tool that does both: `command | head -n 10 | cut -f1,3` chains them. The deeper gap: bash sees text columns, PowerShell sees typed properties.
command | head -n 10Same `head` / `tail` / `cut` toolchain. Zsh adds `(I)` and `(F)` parameter flags for index/array slicing: `${array[1,10]}` takes elements 1..10 from an array — closer to `Select-Object -First 10` than piping to `head`.
command | head -n 10Same external tools. Fish's array slicing (`set arr (command); echo $arr[1..10]`) parallels PowerShell `Select-Object -First 10` for in-shell variables. For pipelines, head/tail still applies.
Get-Process | Select-Object Name, CPU -First 10PowerShell-native cmdlet (alias `select`). Three jobs in one cmdlet: pick PROPERTIES (`-Property Name,CPU`), pick a SLICE (`-First N`, `-Last N`, `-Skip N`, `-Index 0,2,4`), or BOTH. Also creates calculated properties via hashtables: `Select-Object @{Name="MemMB"; Expression={$_.WorkingSet64 / 1MB}}` — closest analogue is `awk` projection but with typed numeric arithmetic.
command | more +1 /eNo native projection tool. `find /n` numbers lines but does not pick them; for "first 10 lines" the closest cmd-only idiom is `for /f "tokens=*" %i in ('command') do ...` with a counter — verbose. Install Git Bash / WSL for `head` / `tail`, or use PowerShell.
Worked examples
Show the first 10 processes from the list
ps -e | head -n 10Get-Process | Select-Object -First 10Project just the name and CPU fields from each process
ps -eo comm,pcpuGet-Process | Select-Object Name, CPUPick the 5 largest files with a calculated MB column
ls -lS | head -n 6 | awk 'NR>1 {printf "%-30s %.2f MB\n", $9, $5/1048576}'Get-ChildItem -File | Sort-Object Length -Descending | Select-Object -First 5 Name, @{Name='SizeMB';Expression={[math]::Round($_.Length/1MB,2)}}Gotchas
- `Select-Object -First N` STOPS the upstream pipeline as soon as it has N items (it sends a `StopUpstreamCommandsException`) — efficient on infinite or expensive streams. But it ALSO stops `-Process` blocks in upstream `ForEach-Object`s without running their `-End` block, which can leak resources opened in `-Begin`. Use `try/finally` in the upstream cmdlet's body, not `-End`, if `Select-Object -First` is downstream.
- `Select-Object -ExpandProperty Foo` is NOT the same as `Select-Object Foo`. `-ExpandProperty` unwraps to the raw VALUE (string, int, array elements); `-Property` wraps in a `PSCustomObject` with property `Foo`. `Get-Process | Select-Object -ExpandProperty Name | Sort-Object` sorts strings; `Get-Process | Select-Object Name | Sort-Object` sorts objects (with one property) — different downstream behaviour.
- Calculated properties accept either `@{Name=...; Expression=...}` or the shorthand `@{n=...; e=...}` — both work, but inconsistency between scripts hurts readability. Pick `Name`/`Expression` for clarity; `n`/`e` is fine in one-liners.
- `Select-Object -Unique` deduplicates by the ENTIRE projected object's `Equals` (which for `PSCustomObject` is reference equality — every projected row is a distinct object), so `Select-Object Name -Unique` mostly behaves correctly (one row per Name), but only because `-Unique` falls back to a structural compare. If you need byte-identical row dedup, prefer `Sort-Object Name -Unique` (key-based, simpler semantics).
- `-Index` is ZERO-based (`-Index 0` is the first item), but `-First` / `-Last` are ONE-based counts. Mixing them in the same pipeline is a known source of off-by-one bugs. Reach for `-First N` when you mean 'top N'; only use `-Index` when you have specific positions in mind.