pretty printのままだと1行1レコードで扱えない

jqはデフォルトで出力を整形(pretty print)するため、1件のオブジェクトが複数行にまたがる。

$ echo '[{"name": "foo", "age": 20}, {"name": "bar", "age": 30}]' | jq '.[]'
{
  "name": "foo",
  "age": 20
}
{
  "name": "bar",
  "age": 30
}

この出力はwhile readのような行単位の処理と相性が悪く、1件のオブジェクトを1行として扱えない。

-cオプションでコンパクトな1行出力にする

-cオプションを指定すると、各要素が改行なしの1行にまとまる。

$ echo '[{"name": "foo", "age": 20}, {"name": "bar", "age": 30}]' | jq -c '.[]'
{"name":"foo","age":20}
{"name":"bar","age":30}

.[]で配列を要素ごとに分解してから-cを適用することで、1件1行の出力になる。この「1行に1つのJSON値」という形式は一般にJSON Lines(JSONL)と呼ばれ、行単位のテキスト処理ツールと組み合わせやすい。

JSON Lines形式をワンライナーで1行ずつ処理する

-cで出力した各行は独立したJSON値なので、while readで1行ずつ取り出し、さらにjqへパイプして個別に処理できる。

$ echo '[{"name": "foo", "age": 20}, {"name": "bar", "age": 30}]' | jq -c '.[]' | \
  while read -r line; do
    name=$(echo "$line" | jq -r '.name')
    echo "processing: $name"
  done
processing: foo
processing: bar

grepなど他のツールと組み合わせる

1行1レコードになっているため、grepやawkのような行単位のツールでもそのままフィルタできる。

$ echo '[{"name": "foo", "age": 20}, {"name": "bar", "age": 30}]' | jq -c '.[]' | grep '"age":30'
{"name":"bar","age":30}

注意点: .[]を挟まないと1行にまとまらない

-cは出力全体をコンパクトにするオプションであり、JSON Lines形式を保証するものではない。配列に対して.[]を挟まずそのまま-cを適用すると、配列全体が1行になってしまう。

$ echo '[{"name": "foo", "age": 20}, {"name": "bar", "age": 30}]' | jq -c '.'
[{"name":"foo","age":20},{"name":"bar","age":30}]

1件ずつ行に分けたい場合は.[]で配列を分解してから-cを適用する必要がある。