LoginSignup
6
5

More than 3 years have passed since last update.

jqが使えなくても大丈夫なJavaScriptシェル芸

Last updated at Posted at 2018-11-27

jqをインストールできなかったり、jqで複雑な加工をさせたいが構文が難しくて覚えられなかったりと、
なんらかの事情でjqを使わずjavascript(Node.js)でJSONを処理したいことが稀にあるかもしれません。

Node.jsは標準入力に弱い?そんな事はありません。ワンライナーで処理できます!

JSONファイルが小さいとき

下記のようなJSONを想定します。1

sample.json
{
  "menu": {
  "id": "file",
    "value": "File",
    "popup": {
      "menuitem": [
      {"value": "New", "onclick": "CreateNewDoc()"},
      {"value": "Open", "onclick": "OpenDoc()"},
      {"value": "Close", "onclick": "CloseDoc()"}
      ]
    }
  }
}

ワンライナーで処理します。

$ cat sample.json | xargs -0 -i node -pe '({})'
{ menu:
   { id: 'file',
     value: 'File',
     popup:
      { menuitem:
         [ { value: 'New', onclick: 'CreateNewDoc()' },
           { value: 'Open', onclick: 'OpenDoc()' },
           { value: 'Close', onclick: 'CloseDoc()' } ] } } }

menuitemのvalueだけを抽出してみます。

## 少しフィルタしてみる
$ cat sample.json | xargs -0 -i node -pe '({}).menu.popup.menuitem.map(_=>_.value)'
[ 'New', 'Open', 'Close' ]

簡単ですね。

大きなJSONを処理できない問題

しかし、この方法だと実は限界があります。
下記のような単純で少しサイズの大きなJSONファイルがあるとします。

sample.big.json
{ docs:
   [ { "_id": "14cd7192b2d699a606bde01c3123c750" },
     { "_id": "14cd7192b2d699a606bde01c3123d685" },
     { "_id": "14cd7192b2d699a606bde01c3123ddd4" },
     { "_id": "14cd7192b2d699a606bde01c3123f441" },
     { "_id": "14cd7192b2d699a606bde01c3123f6df" },
     { "_id": "14cd7192b2d699a606bde01c31240655" },
     { "_id": "14cd7192b2d699a606bde01c31241075" },
     { "_id": "14cd7192b2d699a606bde01c31241d31" },
     { "_id": "14cd7192b2d699a606bde01c31242a44" }, 
     // 省略
    ]
 }

確認すると69KBあります。

## ファイルサイズ(byte)
$ cat sample.big.json | wc  -c
69656

先程の方法でJSONを処理しようとしてみます。

$ cat sample.big.json | xargs -0 -i node -pe '({})'
xargs: argument line too long

引数(与えられたJSONの文字列)が大きすぎると怒られます。

これはシェルが展開した文字列が OSの引数の制限 (正確には execve(2) の制限) を越えると動かなくなるためです。2
OSの引数に指定可能な文字数の最大値は下記で確認できます。

$ getconf ARG_MAX
32000

Git Bash上で実行すると32KBくらいまでみたいです。
ちなみにGCPのCloud Shellだと2MB(2097152byte)でしたので、Git bash相当小さいようですね。。。

大きなJSONに対応する

OSの制限にかからないワンライナーにします。

support-large-json
$ cat <(sed '1s/^/_=/' sample.big.json) <(echo _) | node -p -

{ docs:
   [ { _id: '14cd7192b2d699a606bde01c3123c750' },
     { _id: '14cd7192b2d699a606bde01c3123d685' },
     { _id: '14cd7192b2d699a606bde01c3123ddd4' },
     { _id: '14cd7192b2d699a606bde01c3123f441' },
     { _id: '14cd7192b2d699a606bde01c3123f6df' },
     ... 791 more items ]
 }

docsの_idだけを抽出してみます。

support-large-json
$ cat <(sed '1s/^/_=/' sample.big.json) <(echo '_.docs.slice(0,3).map(v=>v._id)') | node -p -
[ '14cd7192b2d699a606bde01c3123c750',
  '14cd7192b2d699a606bde01c3123d685',
  '14cd7192b2d699a606bde01c3123ddd4' ]

jqのようにきれいに整形

今まで紹介した方法でシンプルに処理できますが出力されるJSONは、ダブルコーテーションが外れ、インデントが微妙になり、大きなリストは「... xxx more items」のように省略されてしまいます。

出力がきれいになるように修正します。

beautifully
$ cat <(sed '1s/^/_=/' sample.big.json) <(echo 'JSON.stringify(_,null,"\t")') | node -p -
{
        "docs": [
                {
                        "_id": "14cd7192b2d699a606bde01c3123c750"
                },
                {
                        "_id": "14cd7192b2d699a606bde01c3123d685"
                },
                {
                        "_id": "14cd7192b2d699a606bde01c3123ddd4"
                },
                {
                        "_id": "14cd7192b2d699a606bde01c3123f441"
                },
                {
                        "_id": "14cd7192b2d699a606bde01c3123f6df"
                },
                {
                        "_id": "14cd7192b2d699a606bde01c31240655"
                },
        //省略しますが全部出力されます
        ]
}

きれいに出力されてますね。
docsの_idだけを抽出してみます。

beautifully
$ cat <(sed '1s/^/_=/' sample.big.json) <(echo 'JSON.stringify(_.docs.slice(0,3).map(v=>v._id),null,"\t")') | node -p -
[
        "14cd7192b2d699a606bde01c3123c750",
        "14cd7192b2d699a606bde01c3123d685",
        "14cd7192b2d699a606bde01c3123ddd4"
]

まとめ

jq を使ったほうが早そうな気……これでNode.jsさえあればjq使わなくても大丈夫ですし、
複雑な加工でも学習コスト0で気軽にJSONを加工できるようになりますね!

他に良い方法があればコメントお願いします。

備考

jq is awesome !

$ cat sample.big.json | jq [.docs[0:3][]._id]
[
  "14cd7192b2d699a606bde01c3123c750",
  "14cd7192b2d699a606bde01c3123d685",
  "14cd7192b2d699a606bde01c3123ddd4"
]
6
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
5