Recipes
Start from the task. Every example uses the released ysh file and ordinary YAML input.
Read one value
ysh '.server.port' config.yml
Use -r when another command needs unquoted scalar text:
host=$(ysh -r '.server.host' config.yml)
Select matching entries
ysh '.services[] | select(.enabled) | .name' config.yml
ysh '.services | filter(.port >= 8000) | map(.name)' config.yml
Add -e when an empty, null, or false result should fail a shell script:
ysh -e '.services[] | select(.name == "api")' config.yml >/dev/null
Change a file safely
Preview the result first:
ysh -o=yaml '.image.tag = "stable"' deploy.yml
Then make the same update atomically:
ysh -i '.image.tag = "stable"' deploy.yml
-i refuses symlinks and only replaces the original after every document succeeds.
Update a repository as one transaction:
ysh --explain=json -i '.image.tag = "stable"' services/*.yml 2>changes.jsonl
Every candidate is prepared before the first replacement. The JSON Lines report records paths and decisions without recording changed values.
Set values from the environment
strenv always creates a string. env parses the variable as YAML.
IMAGE_TAG=stable ysh -i '.image.tag = strenv(IMAGE_TAG)' deploy.yml
LIMITS='{cpu: 2, memory: 512Mi}' ysh '.resources = env(LIMITS)' deploy.yml
Disable environment operators when expressions are untrusted:
ysh --security-disable-env-ops '.services | length' config.yml
Merge defaults with an override
ysh eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' defaults.yml production.yml
The right mapping wins at scalar leaves. This focused eval-all workflow covers configuration layering; it is not yq's entire stream engine.
Work across every document
ysh --all-documents '[documentIndex, .metadata.name]' stream.yml
ysh --document 2 '.spec' stream.yml
Build new YAML
ysh -n -o=yaml '{name: "api", enabled: true, ports: [8080, 8443]}'
Inspect a strange document
ysh --type '.release.created' config.yml
ysh --tag '.widget' config.yml
ysh --line '.services[0].port' config.yml
ysh --events config.yml
ysh --ast config.yml
Use the event stream to see what the parser recognized, then the AST to inspect resolved node identity, tags, anchors, aliases, and merges.
Bound hostile input
ysh \
--max-input-bytes 1048576 \
--max-nodes 20000 \
--max-depth 80 \
--security-disable-env-ops \
'.metadata.name' untrusted.yml
These are rejection ceilings, not tuning hints. Pick limits appropriate to the workload.