The yaml command
yaml checks YAML files, lints them, reads a value out of one, and converts between YAML and JSON. It is fast (a 5 MB file checked in a tenth of a second), so run it on every file you write or change.
brew install kmoneil/tap/yaml
Each release also has the binary for Linux, macOS and Windows on x86_64 and arm64, and zig build -Doptimize=ReleaseSafe builds it from source into zig-out/bin/yaml. yaml --help lists every option.
Check files
Silence and exit 0 mean every document of every file is YAML. Each file that is not says where, from 1, and what is wrong, then the line and a caret under the column:
$ yaml check good.yaml dup.yaml tab.yaml
dup.yaml:2:1: a mapping cannot have two equal keys
a: 2
^
tab.yaml:2:2: a tab character cannot indent a block
b: 1
^
A file stops at its first error: fix it and run again to see the next. For a tool or a CI step, --format json writes the findings as a JSON array, [] when there are none:
$ yaml check --format json dup.yaml
[
{"file":"dup.yaml","line":2,"column":1,"message":"a mapping cannot have two equal keys"}
]
Lint files
yaml lint checks as check does, then warns of YAML that is valid but reads one way here and another in YAML 1.1 tools such as PyYAML, or is likely a mistake. Each warning says how to fix it:
$ yaml lint lint.yaml
lint.yaml:1:10: warning: `yes` is a string in YAML 1.2 but a boolean in YAML 1.1 (PyYAML): quote it, 'yes', if it is a string
enabled: yes
^
lint.yaml:2:7: warning: `0755` is 755 in YAML 1.2 but 493 in YAML 1.1 (PyYAML), which reads a leading 0 as octal: write 755 or 493, as meant
mode: 0755
^
lint.yaml:4:8: warning: the anchor &s is never used: no alias names it
spare: &s {z: 2}
^
lint.yaml:7:1: warning: the key `name` differs from `Name`, on line 6, only in case, which some readers ignore: rename one
name: b
^
| Rule | Warns of |
|---|---|
yaml11 | a plain value YAML 1.1 reads otherwise: yes, on, y, 0755, 1:20, 1e3, 2001-12-14 |
tab | a tab in a line’s indentation |
trailing-space | blanks at the end of a line |
line-length | a line longer than --max-line <n> |
unused-anchor | an anchor no alias names |
key-case | keys of one mapping that differ only in case or trailing blanks |
--allow trailing-space,line-length turns rules off; --fail-on yaml11 exits 1 for that rule’s warnings only. With --format json each finding has a "severity", and each warning its "rule". A GitHub Actions workflow’s on: key is a yaml11 warning, rightly, as PyYAML reads it as true; GitHub reads a string, so quote it or lint workflows with --allow yaml11.
Read a value
yaml get prints the value at a path, as YAML, or as JSON with --json. A path is . for the whole document, or steps: .key, .["a key"], [n] for a sequence’s item.
$ yaml get '.servers[0].name' config.yaml
web
$ yaml get --json .flags config.yaml
{"debug":true,"ratio":0.5}
In a file of several documents, --document <n> (from 0) picks one.
Convert
yaml json prints each document as JSON, one a line, or indented with --indent <n>; yaml from-json prints JSON as YAML, quoting any string a YAML reader would take for something else.
$ yaml json --merge-keys compose.yaml | jq .services
yaml events prints the events a file reads to, in the yaml-test-suite’s notation, to see exactly how it parses.
Options
| Option | What it does |
|---|---|
--schema core\|yaml11\|json\|failsafe | How a plain value with no tag is typed. core (YAML 1.2) by default; yaml11 types it as PyYAML does |
--merge-keys | get and json: apply <<: *base, as most readers do |
--duplicate-keys keep | Keep a mapping’s second equal key instead of refusing it |
--max-depth <n> | The most collections open at once (256) |
A file named - is standard input. UTF-16 and UTF-32 files are read too.
Exit status
0 when every file is YAML and the command did what it was asked; 1 when a file is not YAML or JSON, lint warns, a path has no value, or a value cannot be written as JSON; 2 on a usage error or a file that could not be read.
For AI agents
yaml skill prints a guide for coding agents: each command with an example, the errors seen most and how to fix them. Every example in it is tested. yaml skill --install DIR writes DIR/yaml/SKILL.md, which tells the agent to run yaml skill, so it is never out of date.