基本

基本 の jq

1個の演習

基本について

jqは、入ってきたJSONデータを_単一の式_(_フィルターのパイプライン_として書きます)に通し、目的とする変換結果を得る仕組みです。

jqのコマンドラインオプションの簡単な紹介

jq言語は、jqという_プログラム_によって実装されています。このプログラムには、入力をどのように読み込むか、出力をどのように表示するかを制御する、便利なコマンドラインオプションがいくつか用意されています。

以下の例では、次のオプションが登場します。

  • -n または --null-input

    通常、jqプログラムには読み込むファイルを渡すか、標準入力にデータを送ります。--null-inputオプションを使うと、入力を一切与えずにJSONデータを生成できます。

  • -c または --compact-output

    jqはデフォルトで出力を整形して表示します。データがきれいに整形されていると、人間が読むのにはとても便利です。しかし、機械にとっては必要ありません。--compact-outputオプションは整形用の空白を取り除き、結果のJSONをできるだけ小さくします。

  • -f filename または --from-file filename

    jqプログラムをコマンドラインで渡す代わりに、filenameから読み込みます。sedとawkも、同じ目的で-fオプションを使います。これは、練習用の演習のテストスクリプトで使われています。

すべてのオプションの詳細は、マニュアルを参照してください。

フィルターとパイプ

フィルターは、式とも呼ばれます。

_フィルター_は入力を受け取り、出力を生成します。Unixシェルでの作業と同じように、_フィルター_同士をパイプ|でつなぐと、ある_フィルター_の出力を別の_フィルター_の入力にできます。

恒等フィルター:.

これが最も単純な_フィルター_です。入力をそのまま出力に渡すだけです。たとえば、jqはデフォルトで整形して表示するので、.フィルターにJSONを渡すだけで、きれいに整形された出力がそのまま得られます!

$ echo '[1, 2, 3]' | jq '.'
[
  1,
  2,
  3
]

配列

ここでは配列を扱う方法を簡単に紹介します。この話題は、後でもっと詳しく扱います。

_配列_の要素には角括弧([])でアクセスします。インデックスは0から始まります。

$ echo '[10, 20, 30]' | jq '.[1]'
20

式を[と]で囲むと、_フィルター_で_配列_を作れます。

  • 要素があらかじめ決まっている場合:

    jq -n '[1, 2, 3]'
    
  • 要素のストリームをまとめる場合:たとえば、rangeは数値のストリームを出力する関数です

    $ jq -n 'range(10; 70; 15)'
    10
    25
    40
    55
    

    []を使うと、式の結果を配列にまとめられます

    $ jq -c -n '[range(10; 70; 15)]'
    [10,25,40,55]
    

カンマは演算子です

_カンマ_は、_配列_の要素を区切るだけの記号ではありません。カンマは、ストリームを連結する演算子です。

たとえば[1, 2, 3]は、3つの式1、2、3を連結した結果を、_配列_のコンストラクター[]でまとめる_フィルター_です。

上のrange(10; 70; 15)にあるセミコロンに気づきましたか? jq言語では_カンマ_に特別な役割があるため、複数の引数を取る関数では、引数の区切りにセミコロンを使います。

オブジェクト

オブジェクトを簡単に紹介します。

多くのプログラミング言語と同じように、_オブジェクト_のプロパティにはドットでアクセスします。

$ echo '{"foo": {"bar": "qux"}}' | jq '.foo.bar'
"qux"

_オブジェクト_にも角括弧を使えますが、その場合は文字列リテラルを引用符で囲む必要があります。これは、スペースを含むキーを扱う方法のひとつです。

$ echo '{"foo bar": "qux"}' | jq '.["foo bar"]'
"qux"

_オブジェクト_は、{}とkey: valueのペアで作れます。_キー_が「単純な」文字列であれば、引用符は必要ありません。

jq -n '{question: (6 * 9), answer: 42}'

とすると、次のように出力されます。

{
  "question": 54,
  "answer": 42
}

_キー_を_式_として扱うには、_キー_を括弧(())で囲む必要があります(以下も上と同じ結果を出力します)。

echo '[["question", "answer"], [54, 42]]' \
| jq '{(.[0][0]): .[1][0], (.[0][1]): .[1][1]}'
Note

大きなオブジェクトから一部のキーだけを取り出したいことは、よくあります。 たとえば、次のオブジェクトからidとnameを取り出す場合を考えてみましょう。

{
    "id": 101,
    "name": "alpha widget",
    "specifications": {...}
}

次のように書けます。

{id: .id, name: .name}

しかし、これはよくある操作なので、もっと短く書ける構文が用意されています。

{id, name}

パイプライン

たとえば、次の内容のfile.jsonがあるとします。

{
  "key1": "value1",
  "key2": [5, 15, 25]
}

key2の_配列_の長さを計算してみましょう。

$ jq '.key2 | length' file.json
3

.key2式の出力を、lengthへの入力として_パイプで渡して_います。ご想像どおり、配列の要素数を出力します。

Caution

これは、jqに慣れるまで少し時間がかかる点です。ほとんどの関数は(すべてではありませんが)、データを引数として渡すのではなく、フィルターの入力に渡す_フィルター_のように動作します。

フィルターが入力を無視する場合

次の例では、入力のJSONデータは無視され、出力に影響しません。

$ echo '{"answer": 42}' | jq '6 * 9'
54

フィルターがデータのストリームを出力する場合

_フィルター_は、複数の値を出力できます。たとえば、.[]という_フィルター_は、_配列_の各要素を別々の値として出力します。

$ jq -n -c '[1, 2, 3]'
[1,2,3]

$ jq -n -c '[1, 2, 3] | .[]'
1
2
3

このような_フィルター_を別の_フィルター_にパイプすると、2つ目の_フィルター_が**値ごとに**実行されます。

$ jq -n -c '[1, 2, 3] | .[] | . * 2'
2
4
6

これは、暗黙の繰り返しのようなものです。この仕組みを一度理解すると、とても強力なjqの_フィルター_が、驚くほど短く書けることに気づくでしょう。

括弧

括弧(())は、他の言語と同じように、部分式をまとめて演算の順序を確定するために使います。jqでは、括弧が必要になる場面が意外に思えるかもしれません。

たとえば、2つの要素を持つ_配列_を作りたいとします。9の平方根と、_e_の1乗です。2つの式はそれぞれ9 | sqrtと1 | expです。期待する出力は、配列[3, 2.7...]です。

$ jq -n '[ 9|sqrt, 1|exp ]'
[
  20.085536923187668,
  2.718281828459045
]

なぜ期待どおりにならなかったのでしょうか? jqはこれを次のように解釈します。

[ ((9|sqrt), 1) | exp ]

jqは2つの要素(3と1)のストリームを作り、それぞれがexpに渡されます。

expが入力として受け取る数値を1つだけにする必要があります。つまり、カンマより先にパイプが評価されるようにしなければなりません。

$ jq -n '[ 9|sqrt, (1|exp) ]'
[
  3,
  2.718281828459045
]

型

マニュアルから引用します。

jqは、JSONと同じ種類のデータ型をサポートしています。数値、文字列、真偽値、配列、オブジェクト(JSONの言葉でいえば、文字列のキーだけを持つハッシュ)、そして"null"です。

これらについては、この後の演習で詳しく学びます。

空白

jqでは、空白は意味を持ちません。コードを整形するために、スペース・タブ・改行を自由に使ってください。jqのスタイルガイドは、現在のところ見当たりません。

不変の値

jqの値は_不変_です。値を変更するフィルターは、新しい値を出力します。つまり、jqにはグローバル変数がありません。フィルターからフィルターへ状態を渡すことに慣れる必要があります。

「真偽」

falseとnullという値は偽とみなされます。それ以外の値(数値の0や、空の文字列・配列・オブジェクトを含む)は真です。

関数と演算子

深くは立ち入りませんが(関数は別の演習で扱う話題です)、便利な組み込み関数をいくつか紹介します。

  • length

    入力として配列を受け取り、その配列の要素数を出力します。

    $ jq -n '[10, 20, 30, 40] | length'
    4
    
  • +

    この演算子は、オペランドの型によって動作が変わります。 数値なら加算し、 文字列なら連結し、 配列なら要素を追加し、 オブジェクトならマージします。

    $ jq -c -n '
        3 + 4,
        "foo" + "bar",
        ["a", "b"] + ["c"],
        {"m": 10} + {"n": 20}
    '
    7
    "foobar"
    ["a","b","c"]
    {"m":10,"n":20}
    

    addは、配列を受け取り、+の規則にしたがってすべての要素を足し合わせた結果を返す関数です。[1, 2, 3] | addは6を出力します。

  • map

    入力として配列を、引数としてフィルターを受け取り、各要素にそのフィルターを適用した配列を出力します

    $ jq -c -n '[10, 20, 30, 40] | map(. / 5)'
    [2,4,6,8]
    
  • select

    _何らかの_入力と、引数としてのフィルターを受け取ったとき:

    • 引数として渡されたフィルターを入力に適用した結果が_真_の値になれば、入力をそのまま出力します
    • そうでなければ、_何も_出力しません(nullという値ではなく、本当に何も出力しません)

    たとえば、いくつかの数値から、3で割り切れるものを選んでみましょう

    $ jq -n 'range(10) | select(. % 3 == 0)'
    0
    3
    6
    9
    

    rangeは数値の_ストリーム_を出力するのでした。selectは数値ごとに1回ずつ呼び出されます。式を「通過する」数値だけが出力されます。

    配列の要素を選びたいことはよくあります。方法はいくつかあります。

    入力が["Anne", "Bob", "Cathy", "Dave"]のとき、長さが4の名前を選びます。

    • mapとselectを組み合わせる

      map(select(length == 4))
      
    • 配列を要素に分解し、そのストリームにselectを適用して、結果をまとめる

      [ .[] | select(length == 4) ]
      

コメント

コメントは#で始まり、行の終わりまで続きます。

GitHubで編集 リンクは新しいウィンドウまたはタブで開きます

基本を学習する