How yq Compares to jq, and What Features Are Missing

Updated

yq is a command-line processor that borrows jq's filter syntax but applies it to YAML, JSON, INI, XML, properties, CSV, and TSV files. If you already know jq, most common expressions — .a.b[0].c, assignments like .a.b[0].c = "cool", and strenv() for environment variables — carry over directly. The gaps are at the edges: yq does not yet implement everything jq does, and its handling of comments and whitespace, while deliberate, is not exhaustive. The sections below cover where the two tools align, where they diverge, and what to check before relying on yq for a given task.

Where yq and jq Align

yq's documentation describes it as using "jq like syntax" while working across YAML and other formats, not just JSON. In practice this means:

  • Path expressions work the same way. yq '.a.b[0].c' file.yaml reads a nested value, and piping from STDIN with yq '.a.b[0].c' < file.yaml behaves as you would expect from jq.
  • In-place updates are a first-class operation: yq -i '.a.b[0].c = "cool"' file.yaml rewrites the file directly.
  • Environment variable injection uses the same strenv() function: NAME=mike yq -i '.a.b[0].c = strenv(NAME)' file.yaml.
  • Multi-file merging is supported through load() and reduce expressions, e.g. yq -n 'load("file1.yaml") * load("file2.yaml")', or glob-based merging with yq ea '. as $item ireduce ({}; . * $item)' path/to/*.yml.

The documentation states that yq "does support the most common operations and functions, and more is being added continuously." So for everyday read, update, and merge work, the jq mental model transfers without much friction.

Where yq Diverges from jq

The most important difference is scope of formats. jq is a JSON processor; yq is built for YAML first and extends to JSON, XML, INI, properties, CSV, and TSV. If your pipeline is JSON-only, jq remains the more complete implementation. If your pipeline touches YAML — Kubernetes manifests, CI configs, application settings — yq is the tool that understands the format natively rather than requiring a JSON round-trip.

A second difference is distribution. yq is written in Go, so the documentation notes you can "download a dependency free binary for your platform and you are good to go," with additional options through package managers, Docker, and Podman. That matters in minimal containers or air-gapped environments where installing a runtime is inconvenient.

Known Limitations to Plan Around

The documentation's "Known Issues / Missing Features" section names two concrete caveats:

  1. Comment and whitespace preservation is best-effort. yq "attempts to preserve comment positions and whitespace as much as possible, but it does not handle all scenarios," with a pointer to the underlying go-yaml v3 library for details. If your YAML files carry meaningful comments — annotated configs, documented manifests — verify the output after an in-place edit rather than assuming comments survive untouched.
  2. PowerShell quoting is problematic. The docs note that "Powershell has its own... opinions on quoting yq." On Windows, expect to adjust quoting or escape characters when passing filters, and consult the project's tips and tricks page for common workarounds.

Beyond these, the docs are explicit that yq "doesn't yet support everything jq does." The safe approach: if a filter depends on an advanced jq builtin, test it against your actual yq version before committing it to a script. The documentation index (llms.txt) and the recipes page are the places to confirm current coverage.

Choosing Between Them

Consideration Lean toward jq Lean toward yq
Input format JSON only YAML, XML, INI, properties, CSV/TSV
Filter complexity Advanced or uncommon jq builtins Common path reads, assignments, merges
Comment preservation Not applicable Needed, but must be verified after edits
Deployment Runtime available Prefer a dependency-free binary, Docker, or Podman
Shell environment Bash/zsh PowerShell requires quoting care

A practical pattern is to prototype the filter in jq against a JSON export, then port it to yq once you confirm the expression is within yq's supported set — and re-run it against the real YAML to catch comment or formatting changes.