jqが使えなくても大丈夫なJavaScriptシェル芸
jqが使えなくても大丈夫なJavaScriptシェル芸:
jqをインストールできなかったり、jqで複雑な加工をさせたいが構文が難しくて覚えられなかったりと、
なんらかの事情でjqを使わずjavascript(Node.js)でJSONを処理したいことが稀にあるかもしれません。
Node.jsは標準入力に弱い?そんな事はありません。ワンライナーで処理できます!
下記のようなJSONを想定します。1
ワンライナーで処理します。
簡単ですね。
しかし、この方法だと実は限界があり、
シェルが展開した文字列が OSの制限 (正確には execve(2) の制限) を越えると動かなくなります。2
OSの最大値は下記で確認できます。
Git Bash上で実行すると32KBくらいまでみたいです。
ちなみにGCPのCloud Shellだと2MB(2097152byte)でしたので、Git bash相当小さいような。。。
OSの制限にかからないワンライナーにします。
上の方法だと短いですが、
ダブルコーテーションが外れ、
すこしインデントが微妙になり、
大きなリストは「... xxx more items」のように省略されてしまうので、
きれいに出力するようにします。。。
ここまで来るとjq使ったほうが早そうな気……これでNode.jsさえあればjq使わなくても大丈夫ですし、
複雑な加工でも学習コスト0で気軽にJSONを加工できるようになりますね!
他に良い方法があればコメントお願いします。
jqをインストールできなかったり、jqで複雑な加工をさせたいが構文が難しくて覚えられなかったりと、
なんらかの事情でjqを使わずjavascript(Node.js)でJSONを処理したいことが稀にあるかもしれません。
Node.jsは標準入力に弱い?そんな事はありません。ワンライナーで処理できます!
JSONファイルが小さいとき
下記のようなJSONを想定します。1sample.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()' } ] } } }
## 少しフィルタしてみる
$ cat sample.json | xargs -0 -i node -pe '({}).menu.popup.menuitem.map(_=>_.value)'
[ 'New', 'Open', 'Close' ]
しかし、この方法だと実は限界があり、
シェルが展開した文字列が OSの制限 (正確には execve(2) の制限) を越えると動かなくなります。2
sample.big.json
{ docs:
[ { "_id": "14cd7192b2d699a606bde01c3123c750" },
{ "_id": "14cd7192b2d699a606bde01c3123d685" },
{ "_id": "14cd7192b2d699a606bde01c3123ddd4" },
{ "_id": "14cd7192b2d699a606bde01c3123f441" },
{ "_id": "14cd7192b2d699a606bde01c3123f6df" },
{ "_id": "14cd7192b2d699a606bde01c31240655" },
{ "_id": "14cd7192b2d699a606bde01c31241075" },
{ "_id": "14cd7192b2d699a606bde01c31241d31" },
{ "_id": "14cd7192b2d699a606bde01c31242a44" },
// 省略
]
}
## ファイルサイズ(byte)
$ cat sample.big.json | wc -c
69656
$ cat sample.big.json | xargs -0 -i node -pe '({})'
xargs: argument line too long
$ getconf ARG_MAX 32000
ちなみにGCPのCloud Shellだと2MB(2097152byte)でしたので、Git bash相当小さいような。。。
JSONファイルが大きいとき
OSの制限にかからないワンライナーにします。$ cat <(sed '1s/^/_=/' sample.big.json) <(echo _) | node -p -
{ docs:
[ { _id: '14cd7192b2d699a606bde01c3123c750' },
{ _id: '14cd7192b2d699a606bde01c3123d685' },
{ _id: '14cd7192b2d699a606bde01c3123ddd4' },
{ _id: '14cd7192b2d699a606bde01c3123f441' },
{ _id: '14cd7192b2d699a606bde01c3123f6df' },
... 791 more items ]
}
## すこしフィルタしてみる
$ cat <(sed '1s/^/_=/' sample.big.json) <(echo '_.docs.slice(0,3).map(v=>v._id)') | node -p -
[ '14cd7192b2d699a606bde01c3123c750',
'14cd7192b2d699a606bde01c3123d685',
'14cd7192b2d699a606bde01c3123ddd4' ]
jqのようにきれいに整形
上の方法だと短いですが、ダブルコーテーションが外れ、
すこしインデントが微妙になり、
大きなリストは「... xxx more items」のように省略されてしまうので、
きれいに出力するようにします。。。
$ 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"
},
//省略しますが全部出力されます
]
}
## 少しフィルタしてみる
$ cat <(sed '1s/^/_=/' sample.big.json) <(echo 'JSON.stringify(_.docs.slice(0,3).map(v=>v._id),null,"\t")') | node -p -
[
"14cd7192b2d699a606bde01c3123c750",
"14cd7192b2d699a606bde01c3123d685",
"14cd7192b2d699a606bde01c3123ddd4"
]
複雑な加工でも学習コスト0で気軽にJSONを加工できるようになりますね!
他に良い方法があればコメントお願いします。
備考
$ cat sample.big.json | jq [.docs[0:3][]._id] [ "14cd7192b2d699a606bde01c3123c750", "14cd7192b2d699a606bde01c3123d685", "14cd7192b2d699a606bde01c3123ddd4" ]
コメント
コメントを投稿