jq — Filter, transform, and build JSON from the shell with a tiny query DSL across all 5 shells
Equivalents in every shell
curl -s https://api.example.com/users | jq '.[] | .name'Selectors: `.` (identity), `.field` (property), `.[0]` (array index), `.[]` (iterate), `select(...)` (filter), `|` (pipe), `to_entries` / `from_entries` (object reshape). `-r` strips quotes for raw string output when feeding shell tools; `-c` emits one-line compact JSON; `-n` builds from scratch with `--arg`/`--argjson`.
curl -s https://api.example.com/users | jq '.[] | .name'Same external `jq`. Not in base macOS — `brew install jq`. Zsh's `${(f)var}` line-split modifier turns `jq -r`'s newline-separated output into a zsh array: `users=("${(@f)$(jq -r .[].name <users.json)}")`.
curl -s https://api.example.com/users | jq '.[] | .name'Same external `jq`. Fish's `string split \n` pairs cleanly with `jq -r` output to produce a fish list iterable via `for x in (jq -r .[].name <users.json | string split \n)`.
Invoke-RestMethod https://api.example.com/users | ForEach-Object { $_.name }PowerShell's `Invoke-RestMethod` + property-access pipeline is the native equivalent — no separate parser binary. `ConvertFrom-Json` exposes the same model from a string input. For complex queries that mirror `jq`'s DSL, install jq directly: `winget install jqlang.jq`. Property access is case-INSENSITIVE; jq is case-sensitive.
curl -s https://api.example.com/users | jq ".[] | .name"Windows 10 1803+ ships `curl.exe`; `jq` needs separate install via `winget install jqlang.jq` or `choco install jq`. Inside cmd.exe, double-quote the jq expression (`".[]"`) — single quotes don't group in cmd, and `%` chars must be doubled (`%%`).
Worked examples
Filter an array of objects by property
jq '.[] | select(.role == "admin") | .name' users.json(Get-Content users.json | ConvertFrom-Json) | Where-Object role -eq admin | Select-Object -ExpandProperty namejq ".[] | select(.role == \"admin\") | .name" users.jsonBuild a new JSON object from shell variables
jq -n --arg name alice --argjson age 30 '{name: $name, age: $age, created: now | todate}'@{name='alice'; age=30; created=([DateTime]::UtcNow).ToString("o")} | ConvertTo-JsonReshape every object in an array (rename + add computed field)
jq '[.[] | {id, fullName: (.first + " " + .last)}]' users.json(Get-Content users.json | ConvertFrom-Json) | ForEach-Object { [PSCustomObject]@{id=$_.id; fullName="$($_.first) $($_.last)"} } | ConvertTo-JsonGotchas
- Default output is JSON (quoted strings, escaped); `-r` (raw) strips the surrounding quotes — essential when piping to other shell tools that don't want a `"value"` literal. Forgetting `-r` is the #1 newcomer trap (e.g. `cd $(jq .path config.json)` will try to `cd` into `"some/path"` with literal quotes).
- `jq` is case-SENSITIVE on property names (matches the JSON spec). PowerShell's `[PSCustomObject]` is case-INSENSITIVE by default — porting `jq` queries to `Where-Object` may produce different behaviour if your JSON has same-letter different-case keys. Use `ConvertFrom-Json -AsHashtable` (pwsh 6+) for case-sensitive equivalence.
- Filter expressions are NOT general-purpose JavaScript — `jq` has its own DSL (closer to XPath). `.field?` (optional) silently skips missing keys; `.field` errors. `|=` (update assignment), `//` (alt default), `?` (optional path), and `try ... catch` are jq-specific and have no jq-to-JavaScript translation.
- Streaming mode (`--stream` / `--stream-errors`) is mandatory for JSON files larger than RAM. Without it, jq buffers the entire input — a 5GB JSON file at default settings can OOM a 4GB container. `--stream` emits `[path, value]` tuples — different processing pattern but constant memory.
- jq 1.7 (2023) changed module loading syntax (`include "file";` instead of `include "file" {};`) and added `pick`, `getpath`, and `splits` builtins. Older Ubuntu LTS ships jq 1.6 — scripts using new builtins fail with `jq: error: pick/1 is not defined`. Pin jq version in CI / Dockerfiles.
WSL & PowerShell Core notes
Common tasks using jq
- Convert a CSV file to JSON
Parse a CSV (with header row) and emit an array of JSON objects keyed by header.
- Minify a JSON file
Strip whitespace and indentation from a JSON file to produce the smallest valid output.
- Parse JSON from a shell
Extract specific fields from a JSON blob (a curl response, kubectl output, log line) and pipe them into other shell tools — the everyday "grab the .token / .items[].name / .data.url" workflow that differs sharply from just pretty-printing.
- URL-encode or decode a string
Percent-encode a string for safe inclusion in a URL query, or decode an already-percent-encoded string back to its original characters — for building API query parameters, decoding `Location:` redirect headers, and inspecting OAuth callback URLs.