yq — Selection

select() — Conditional Filtering

select(condition) keeps values where the condition is true and discards the rest. This is the primary filtering mechanism in yq/jq.

Select entries matching a value pattern
yq '.asciidoc.attributes | to_entries[] | select(.value | test("10\\.50\\.1\\."))' docs/antora.yml | head -20
# Output (each entry is a key/value pair):
# key: ise-ip
# value: "10.50.1.20"
# ---
# key: vault-ip
# value: "10.50.1.60"
# ...

to_entries converts a map {k: v} into an array of {key: k, value: v} objects. The [] iterator produces each entry individually so select() can filter.

test() — Regex Matching

Filter attribute keys by regex
yq -r '.asciidoc.attributes | to_entries[] | select(.key | test("^port-")) | .key' docs/antora.yml
# Output:
# port-https
# port-ssh
# port-kerberos
# port-ldap
# port-ldaps
# port-gc
# port-dns
# port-dhcp-server
# port-dhcp-client
# port-ntp
# port-smtp
# port-submission
# port-imaps

test("regex") returns true/false without capturing. Anchors work: ^ for start, $ for end.

Filter keys matching multiple patterns with alternation
yq -r '.asciidoc.attributes | to_entries[] | select(.key | test("^(ise|vault)-")) | "\(.key): \(.value)"' docs/antora.yml
# Output:
# ise-hostname: ise-01.inside.domusdigitalis.dev
# ise-ip: 10.50.1.20
# ise-ers-port: 9060
# ise-openapi-port: 443
# ise-mnt-port: 443
# ise-dc-port: 2484
# ise-pxgrid-port: 8910
# vault-hostname: vault-01.inside.domusdigitalis.dev
# vault-01-hostname: vault-01.inside.domusdigitalis.dev
# vault-ip: 10.50.1.60
# vault-01-ip: 10.50.1.60
# vault-port: 8200
# vault-pki-path: pki_int
# vault-ssh-path: ssh
# vault-kv-path: kv
Case-insensitive regex
yq -r '.asciidoc.attributes | to_entries[] | select(.key | test("vyos"; "i")) | "\(.key): \(.value)"' docs/antora.yml
# Output:
# vyos-vip: 10.50.1.1
# vyos-01-hostname: vyos-01.inside.domusdigitalis.dev
# vyos-01-ip: 10.50.1.2
# vyos-02-hostname: vyos-02.inside.domusdigitalis.dev
# vyos-02-ip: 10.50.1.3

The second argument to test() is a flags string. "i" is case-insensitive; "x" is extended regex.

map(select()) — Array Filtering

Filter and collect into an array
yq '.asciidoc.attributes | to_entries | map(select(.key | test("^ws-")))' docs/antora.yml
# Output:
# - key: ws-razer-hostname
#   value: modestus-razer.inside.domusdigitalis.dev
# - key: ws-razer-short
#   value: modestus-razer

The difference: to_entries[] with select() streams individual matches. to_entries | map(select()) collects matches back into an array. Use map() when you need the result as a list for further processing.

Count matches
yq '.asciidoc.attributes | to_entries | map(select(.key | test("^port-"))) | length' docs/antora.yml
# Output: 13

has() in Selection

Select entries that have a specific nested key
cat <<'YAML' > /tmp/services.yml
services:
  ise:
    ip: 10.50.1.20
    port: 443
    description: Identity Services Engine
  vault:
    ip: 10.50.1.60
    port: 8200
  dns:
    ip: 10.50.1.90
    port: 53
    description: BIND9 resolver
YAML
yq '.services | to_entries[] | select(.value | has("description")) | .key' /tmp/services.yml
# Output:
# ise
# dns

Negation — select(…​ | not)

Find attributes that do NOT match a pattern
yq '.asciidoc.attributes | to_entries | map(select(.key | test("^(port|ise|vault|ws|vyos|ad|kvm|nas|bt|mail|pfsense|wlc|keycloak|k3s)-") | not)) | length' docs/antora.yml
# Output: ~60 (non-infrastructure attributes)

| not inverts the boolean. This is the yq equivalent of grep -v.

List non-infrastructure attributes
yq -r '.asciidoc.attributes | to_entries[] | select(.key | test("-ip$|-hostname$|-port$|-mac$") | not) | select(.key | test("^(stats|focus|origin|level|type|status|nav|current)")) | "\(.key): \(.value)"' docs/antora.yml | head -10
# Filters to metadata-class attributes only

Combining Conditions

AND — both conditions must match
yq -r '.asciidoc.attributes | to_entries[] | select((.key | test("^port-")) and (.value | test("^[0-9]{2}$"))) | "\(.key): \(.value)"' docs/antora.yml
# Output: ports with 2-digit values (e.g., port-dns: 53, port-ssh: 22, port-ntp: 25)
OR — either condition matches
yq -r '.asciidoc.attributes | to_entries[] | select((.key | test("^cert-")) or (.key | test("^key-"))) | "\(.key): \(.value)"' docs/antora.yml
# Output:
# cert-dir: /etc/ssl/certs
# key-dir: /etc/ssl/private

String Contains (alternative to test)

contains() for literal substring match
yq -r '.asciidoc.attributes | to_entries[] | select(.value | type == "!!str" and contains("domusdigitalis")) | .key' docs/antora.yml | head -10
# Output: all attributes containing the domain string

contains() does literal matching — no regex. Faster than test() when you do not need pattern matching.