yq — YAML / JSON / TOML / XML query and edit on CLI — mikefarah Go vs kislyuk Python; jq-shaped syntax; pwsh ConvertFrom-Yaml + powershell-yaml module across all 5 shells
Equivalents in every shell
yq '.spec.replicas' deployment.yamlTWO competing yq implementations — choose deliberately. **mikefarah/yq** (Go, the more popular one, default in most package managers since ~2022): full YAML editing including comments preservation, jq-LIKE syntax (subset compatible — but `yq eval` vs `jq` are not 100% interchangeable). Install: `brew install yq`, `snap install yq`, or download binary. **kislyuk/yq** (Python wrapper around jq): full jq-syntax compatible but YAML-IN / JSON-out-or-roundtrip — does NOT preserve comments. Install: `pip install yq` (note: same binary name `yq` — they CONFLICT). Detect which you have: `yq --version` → `mikefarah` line means Go; `yq <version>` with no name means Python. mikefarah's `eval` subcommand handles read-only queries; `eval -i` for in-place edits. Default operation since 4.x: `yq <expression> <file>` is `eval` (drop the `eval` keyword).
yq '.spec.replicas' deployment.yamlyq '.spec.replicas' deployment.yamlInstall-Module powershell-yaml; ConvertFrom-Yaml (Get-Content deployment.yaml -Raw) | Select-Object -ExpandProperty spec | Select-Object -ExpandProperty replicaspwsh-native: the `powershell-yaml` module (`Install-Module powershell-yaml -Scope CurrentUser`) provides `ConvertFrom-Yaml` / `ConvertTo-Yaml` cmdlets that emit PSObjects — fully pipeline-native. NO jq-like query language; YAML becomes regular pwsh objects, you query with `.spec.replicas` property access. CAVEAT: comments are NOT preserved on roundtrip (pwsh objects don't carry source comments). For round-trip-with-comments preservation on Windows: install the `yq` binary (Go version) via `winget install MikeFarah.yq` or `scoop install yq`, then use it identically to the Linux/macOS form. pwsh 7.4 added native `ConvertTo-Json` / `ConvertFrom-Json` — but NO ConvertTo-Yaml in core; the module is still required.
yq ".spec.replicas" deployment.yamlAfter `winget install MikeFarah.yq` (Go binary) or `scoop install yq`. Note the QUOTING: cmd does not handle single-quotes the way bash does — wrap the expression in double-quotes (`"…"`). For complex expressions with embedded double-quotes, escape with `\"`: `yq ".items[] | select(.name == \"web\")"`. Alternative on Windows: `powershell -Command "Get-Content deployment.yaml -Raw | ConvertFrom-Yaml | … "` — calls into the pwsh module from cmd.
Worked examples
Extract a single field from a YAML file
yq '.spec.replicas' deployment.yamlyq '.spec.replicas' deployment.yaml(ConvertFrom-Yaml (Get-Content deployment.yaml -Raw)).spec.replicasyq ".spec.replicas" deployment.yamlIn-place edit (set a field)
yq -i '.spec.replicas = 3' deployment.yamlyq -i '.spec.replicas = 3' deployment.yaml$y = ConvertFrom-Yaml (Get-Content deployment.yaml -Raw); $y.spec.replicas = 3; ConvertTo-Yaml $y | Set-Content deployment.yamlyq -i ".spec.replicas = 3" deployment.yamlConvert YAML to JSON
yq -o=json deployment.yamlyq -o=json deployment.yamlConvertFrom-Yaml (Get-Content deployment.yaml -Raw) | ConvertTo-Json -Depth 100yq -o=json deployment.yamlFilter array items by predicate
yq '.items[] | select(.kind == "Service")' resources.yamlyq '.items[] | select(.kind == "Service")' resources.yaml(ConvertFrom-Yaml (Get-Content resources.yaml -Raw)).items | Where-Object kind -eq "Service"yq ".items[] | select(.kind == \"Service\")" resources.yamlGotchas
- **The TWO `yq` binaries are SOURCE OF MAXIMUM CONFUSION** — `mikefarah/yq` (Go, default in `brew install yq`, `snap install yq`, `winget install MikeFarah.yq`) and `kislyuk/yq` (Python, `pip install yq`). Same binary name, DIFFERENT command-line semantics. mikefarah: `yq '.field' file.yaml` (one-shot eval). kislyuk: `yq '.field' file.yaml` (also works — wraps jq). But mikefarah supports `-i` in-place edit; kislyuk does not. mikefarah preserves comments on round-trip; kislyuk does not. mikefarah: `yq --version` prints `version 4.x` with `Mike Farah` in the help text. kislyuk: prints just a version with no author. ALWAYS check `yq --help | head` before assuming syntax. CI scripts that work on one developer's machine often break on another's because of this split.
- **Comments and YAML anchors: only mikefarah/yq preserves them.** YAML's killer feature over JSON is comments + anchors (`&anchor` / `*alias`) + multi-document files (`---` separator). When you `yq -i '.spec.replicas = 3' deployment.yaml`, mikefarah/yq PRESERVES all comments and the original formatting. Python yq, jq-piped-through-Python, and pwsh `powershell-yaml` ALL strip comments on roundtrip. For Kubernetes manifests / GitOps repos where comments document intent ("# this MUST stay 3 for HA"), comment-preservation is mandatory — use mikefarah. For one-shot data extraction (read-only): all implementations are equivalent.
- **Quoting: bash single-quotes are safest; cmd needs double-quotes; both interact with yq's expression parser.** yq expressions often contain `.`, `[`, `]`, `|`, `()` — all of which have shell meanings. Bash/zsh/fish: ALWAYS single-quote the expression (`'.foo[0].bar'`) so the shell passes it verbatim. cmd: cmd has no literal-single-quote — use double-quotes (`".foo[0].bar"`) and escape any embedded `"` as `\"`. The most-painful trap: `select(.name == "web")` — in cmd, the embedded `"` needs `\"` AND the outer wrap needs `"` — `yq ".items[] | select(.name == \"web\")" file.yaml`. In pwsh, single AND double-quotes both work (pwsh treats them similarly to bash), with `'` preferred for verbatim and `"` for variable expansion.
- **TOML and XML support — mikefarah only.** `yq -p toml '.package.name' Cargo.toml` parses TOML (mikefarah 4.30+); `yq -p xml '.root.element' file.xml` parses XML. Use `-p` (parse format) when the file extension is misleading or the parser autodetect fails. Output format: `-o=json` / `-o=yaml` / `-o=toml` / `-o=xml` / `-o=props` (Java properties). The full power: `yq -p xml -o=json file.xml` cross-format converts XML → JSON via mikefarah's internal canonical form. CAVEAT: XML round-trip is LOSSY — attributes vs children get mapped to JSON-shaped trees, attribute namespaces are flattened. Use a dedicated XML tool (`xmllint`, `xq`) for XML round-trip workflows.
- **Pipe interoperability with jq is partial, not total.** Many tutorials say "use yq like jq" — mostly true, with corners. Both support `.field`, `[]`, `select()`, `|`, `length`, `keys`, `map()`. yq adds YAML-specific operators like `with`, `style`, `tag`. jq has some functions yq doesn't (`@base64d`, `@uri`, complex string-interpolation). For "convert YAML to JSON then process with jq": `yq -o=json file.yaml | jq '<expression>'` — pipes the YAML through yq just for format-conversion, then runs the actual transform in jq. This is the most-portable pattern when working with mixed teams (some have jq + Python yq, others have jq + Go yq).