yqコマンドとは?
yqは、構造化されたデータファイルをコマンドラインで検索・抽出・編集できるツールです。
YAML, JSON, XMLなど多くの形式に対応していますが、本記事ではYAML (.yaml, .yml) ファイルに対する操作について解説します。
インストール
以下のコマンドでインストールできます。
yq --versionを実行し、バージョン情報が出力されればインストールは成功です。
Windows
wingetでインストールできます。
winget install --id MikeFarah.yq
macOS
brewでインストールできます。
brew install yq
Linux
snapが使える場合はsnapコマンドで、そうでない場合はwgetでバイナリを直接配置します。
# snapでインストール
sudo snap install yq
# バージョンを指定してバイナリを直接配置
VERSION=v4.47.1
sudo wget https://github.com/mikefarah/yq/releases/download/${VERSION}/yq_linux_amd64 -O /usr/local/bin/yq
sudo chmod +x /usr/local/bin/yq
aptでyqをインストールしようとすると、同名の別のツールがインストールされてしまうので、注意してください。
基本的な構文
基本的には以下の構文で使用します。
yq [コマンド] [オプション] '式' ファイル
このうち中心となるのが式で、処理の具体的な内容を記述します。
コマンド
コマンドは省略が可能であり、省略した場合はevalとなります。
本記事ではevalのみ扱います。
| コマンド | エイリアス | 動作 |
|---|---|---|
eval |
e |
各ファイルの各ドキュメントに対して式を順番に適用 |
eval-all |
ea |
全ファイルの全ドキュメントを一度に読み込み、式を1回だけ実行 |
shell-completion |
補完スクリプトを生成 |
主なオプション
-P -o=yamlは-Poyのように省略して書けます。
| コマンド | エイリアス | 動作 |
|---|---|---|
--inplace |
-i |
ファイルを上書き |
--null-input |
-n |
入力無しで式を評価 |
--input-format |
-p |
入力形式の指定 (yaml, jsonなど) |
--output-format |
-o |
出力形式の指定 |
--prettyPrint |
-P |
出力を整形 |
--indent |
-I |
インデント幅の指定。既定は2 |
--no-doc |
-N |
先頭の---を出力しない |
--no-colors |
-M |
色を付けない |
式の書き方
パス指定
.でマップのキーを辿り、[]で配列を扱います。
| 記法 | 意味 |
|---|---|
. |
ルートノード (ファイル全体) |
.key |
マップのキー |
.key1.key2 |
階層を下に辿る |
.key[0] |
配列の0番目の要素 |
.key[] |
配列の全要素を1つずつ展開 |
.key[0:2] |
配列のスライス (0番目以上2番目未満) |
.["key"] |
キー名を文字列として指定 |
キー名に空白や.を含む場合は正しく解釈されないため、引用符"で囲みます。
再帰的な走査
..と...は、階層を全て降りてノード(要素)を1つずつ展開します。
深い階層のどこにあるか分からない値を探すときに便利です。
| 記法 | 意味 |
|---|---|
.. |
マップのキーを除く全ノード |
... |
マップのキーを含む全ノード |
パイプ
パイプ|は、左側の式の出力を右側の式の入力として渡します。
式A | 式Aの出力を入力とする式
収集
式の全体を[ ]で囲むと、複数行に分かれて出力される結果を1つの配列にまとめられます。
演算子
式の中では組み込み演算子が使用できます。
多くはパイプの右側に置き、左から渡されたノードに対して働きます。
| 演算子 | 説明 |
|---|---|
keys |
マップのキー一覧 |
length |
要素数または文字数 |
kind |
ノードの種類 (map, seq, scalar) |
tag |
YAMLの型タグ (!!str, !!int, !!seqなど) |
path |
ルートからのパスを配列で返す |
parent |
親ノード |
is_key |
マップのキーかどうか |
line, column
|
ファイル中での位置 |
select(式) |
式がtrueになるノードだけを残す |
has("キー") |
そのキー (配列なら添字) を持つかを真偽値で返す |
join("区切り") |
配列の要素を区切り文字で連結して文字列にする |
not |
真偽値を反転 |
代入・削除の演算子
値を書き換える演算子もあります。
| 演算子 | 説明 |
|---|---|
= |
右辺の値を代入する。存在しないキーは新しく作られる |
|= |
左辺の現在値を.として受け取り、その計算結果を代入する |
+= |
配列なら要素の追加、数値なら加算、文字列なら連結 |
* |
2つのマップを再帰的にマージする |
del(パス) |
指定したノードを削除する |
=と|=の違いは、右辺で現在の値を使えるかどうかです。
.score = 10は無条件に10を入れますが、.score |= . + 10は現在の値に10を足した値を入れます。
これらの演算子は、既定では結果が標準出力に表示されるだけで、ファイルは変更されません。
ファイルそのものを書き換えたい場合は-iを付けてください。
使用例
以下ではsample.yamlに対して操作していきます。
project: demo
version: 1
tags: [alpha, beta]
items:
- name: alpha
score: 72
active: true
- name: beta
score: 91
active: false
値を取得する
.でパスを辿り、必要な値だけを取り出します。
yq '.project' sample.yaml # demo
yq '.version' sample.yaml # 1
yq '.items[0].name' sample.yaml # alpha
yq '.items[]' sample.yaml # itemsの全要素を展開
yq '.items[0:2]' sample.yaml # 0番目と1番目を取り出す
yq '.items[].["name"]' sample.yaml # キー名を文字列として指定
演算子をパイプでつなぐと、要素数やキー一覧も取得できます。
yq '.items | length' sample.yaml # 2
yq '.items[0] | keys' sample.yaml
keysの実行結果は以下のようになります。
- name
- score
- active
条件で絞り込む
selectとパイプ|を使うことで、配列の中から条件に合う要素だけを取り出せます。
# scoreが80より大きい要素だけを残す
yq '.items[] | select(.score > 80)' sample.yaml
# activeがtrueの要素のnameを取得
yq '.items[] | select(.active) | .name' sample.yaml # alpha
# nameが"beta"の要素を取得
yq '.items[] | select(.name == "beta")' sample.yaml
hasと組み合わせると、特定のキーを持つノードだけを絞り込めます。
# 各要素がscoreキーを持つか
yq '.items[] | has("score")' sample.yaml # true, true
# scoreキーを持つ要素だけを残す
yq '.items[] | select(has("score"))' sample.yaml
# notでscoreキーを持たない要素だけを抽出
yq '.items[] | select(has("score") | not)' sample.yaml
# []で囲むことで配列として出力
yq '[.items[] | select(.score > 80) | .name]' sample.yaml
パイプを何段もつなぐと、より細かい検索や操作が可能となります。
# 全ノードからscoreキーを持つノードのみ抽出し、そのパスを.で連結して出力する
yq '.. | select(has("score")) | path | join(".")' sample.yaml
実行結果は以下のようになります。
items.0
items.1
値を書き換える
代入・削除の演算子で、YAMLの内容を編集して出力できます。
# versionを2にする
yq '.version = 2' sample.yaml
# 全要素のscoreに10を加える
yq '.items[].score |= . + 10' sample.yaml
# tagsに要素を追加する
yq '.tags += ["gamma"]' sample.yaml
# itemsに要素を1つ追加する
yq '.items += {"name": "gamma", "score": 55, "active": true}' sample.yaml
# 存在しないキーは新しく作られる
yq '.owner.name = "ken"' sample.yaml
# items[0]のactiveキーを削除する
yq 'del(.items[0].active)' sample.yaml
# scoreが80未満の要素を削除する
yq 'del(.items[] | select(.score < 80))' sample.yaml
出力内容だけでなくファイルそのものを書き換えるには-iを付けます。
yq -i '.version = 2' sample.yaml
ゼロからYAMLを作る
-n (--null-input) は入力ファイルを読まずに式だけを評価するので、新しいYAMLをその場で組み立てられます。
yq -n '.project = "new" | .items = [{"name": "alpha"}]'
project: new
items:
- name: alpha
複数ドキュメントを扱う
このセクションでは以下のYAMLを使用します。
name: a
value: 1
---
name: b
value: 2
YAMLは、---で区切ることで1つのファイルに複数のドキュメントを書くことができます。
既定のコマンドはevalのため、それぞれのドキュメントに対して式を評価できます。
ドキュメントの区切りを表す---が出力に不要な場合は、-Nをオプションに渡します。
yq -N '.name' multi.yaml
a
b
特定のドキュメントだけが欲しい場合は、ドキュメント全体にselectを適用します。
# nameキーがbであるドキュメントのみ抽出
yq 'select(.name == "b")' multi.yaml
おわりに
本記事ではYAMLに絞って解説しましたが、冒頭で触れた通り、-pや-oを指定することで他の形式も扱えます。
わざわざエディタを起動することなく簡単に編集や検索ができるため、習得するととても便利だと感じました。