在前面两篇文章中,咱们分别介绍了快速读取 JSON 值的库gjson
和快速设置 JSON 值的库sjson
。今天咱们介绍它们的做者tidwall的一个基于gjson
和sjson
的很是实用的命令行工具jj
。它是使用 Go 编写的快速读取和设置 JSON 值的命令行程序。git
Mac 上能够直接使用brew install tidwall/jj/jj
安装。其余系统能够经过下载编译好的可执行程序,下载地址为https://github.com/tidwall/jj/releases。github
我选择使用go get
安装:golang
$ go get github.com/tidwall/jj/cmd/jj
上面命令执行完成以后,编译生成的jj
程序会放在$GOPATH/bin
目录中,我习惯把$GOPATH/bin
加入系统可执行目录$PATH
中,故能够直接使用。shell
简单的读取和设置(个人环境为 Win10 + Git Bash):json
$ echo '{"name":{"first":"li","last":"dj"}}' | jj name.last dj $ echo '{"name":{"first":"li","last":"dj"}}' | jj -v dajun name.last {"name":{"first":"li","last":"dajun"}}
经过键路径来指定读取/设置的位置,上面第一个命令读取字段name.last
,返回dj
。数组
-v
选项指定设置的值。第二个命令将字段name.last
设置为dajun
,输出设置以后的 JSON 串。键路径在前两篇文章中有详细的介绍,不熟悉的能够回去看一下。微信
实际上读取和设置的语法和形式与咱们前面介绍gjson
和sjson
提到的基本同样,只不过是在命令行上完成的而已。app
读取不存在的字段,返回null
:工具
$ echo '{"name":{"first":"li","last":"dj"}}' | jj name.middle null
读取一个对象类型的字段,返回该对象的 JSON 表示:性能
$ echo '{"name":{"first":"li","last":"dj"}}' | jj name {"first":"li","last":"dj"}
使用索引(从 0 开始)读取数组的元素,非法的索引将返回空:
$ echo '{"fruits":["apple","orange","banana"]}' | jj fruits.1 orange $ echo '{"fruits":["apple","orange","banana"]}' | jj fruits.3
使用索引设置数组的元素,下面命令将数组fruits
的第二个元素设置为pear
:
$ echo '{"fruits":["apple","orange","banana"]}' | jj -v pear fruits.1 {"fruits":["apple","pear","banana"]}
使用-1
或数组长度做为索引,能够在数组后添加一个元素。若是索引超过了数组长度,则会多必定数量的null
:
$ echo '{"fruits":["apple","orange","banana"]}' | jj -v strawberry fruits.-1 {"fruits":["apple","orange","banana","strawberry"]} $ echo '{"fruits":["apple","orange","banana"]}' | jj -v grape fruits.3 {"fruits":["apple","orange","banana","grape"]} $ echo '{"fruits":["apple","orange","banana"]}' | jj -v watermelon fruits.5 {"fruits":["apple","orange","banana",null,null,"watermelon"]}
使用选项-D
删除指定键路径上的元素,若是对应元素不存在,则无效果:
$ echo '{"name":"dj","age":18}' | jj -D age {"name":"dj"} $ echo '{"fruits":["apple","orange","banana"]}' | jj -D fruits.2 {"fruits":["apple","orange"]} $ echo '{"fruits":["apple","orange","banana"]}' | jj -D fruits.5 {"fruits":["apple","orange","banana"]}
第 1 个命令删除字段age
;第 2 个命令删除数组fruits
的第 2 个元素;第 3 个命令删除数组fruits
的第 5 个元素,因为数组长度只有 3,故无效果。
jj
支持从文件中读取 JSON 串和将结果写到文件中。使用选项-i
指定输入文件,选项-o
指定输出文件。下面将从文件fruits.txt
中读取 JSON 串,取数组的第 2 个元素,写到out.txt
中:
$ jj -i fruits.txt -o out.txt fruits.1
fruits.txt
的文件内容以下:
{"fruits":["apple","orange","banana"]}
执行命令,输出文件的内容为:
orange
jj
支持将输出的 JSON 串进行必定的格式化。选项-u
移除全部的空白符,节省存储空间。选项-p
美化格式,便于阅读。
$ echo '{"name":{"first": "li", "last":"dj"}, "age":18}' | jj -u name {"first":"li","last":"dj"} $ echo '{"name":{"first": "li", "last":"dj"}, "age":18}' | jj -p name { "first": "li", "last": "dj" }
与另外一个 JSON 的命令行工具jq
相比,jj
是其性能的 10 倍以上。由于jj
不会验证 JSON 串的有效性,而且它只关心键路径指定的值,一旦该值处理完成就中止。这里有性能对比:https://github.com/tidwall/jj#performance
jj
一个很方便的用途在于日志处理,当前不少日志库都支持 JSON 的格式,例如前面咱们介绍的logrus
。咱们可使用jj
在这些日志中找到相应的信息。咱们先用logrus
生成 20 条玩家登录和下线的日志:
package main import "github.com/sirupsen/logrus" func main() { logrus.SetFormatter(&logrus.JSONFormatter{}) for i := 1; i <= 10; i++ { logrus.WithFields(logrus.Fields{ "userid": i, }).Info("login") logrus.WithFields(logrus.Fields{ "userid": i, }).Info("logoff") } }
生成日志存储在log.txt
文件中:
{"level":"info","msg":"login","time":"2020-03-26T23:36:04+08:00","userid":1} {"level":"info","msg":"logoff","time":"2020-03-26T23:36:04+08:00","userid":1} {"level":"info","msg":"login","time":"2020-03-26T23:36:04+08:00","userid":2} {"level":"info","msg":"logoff","time":"2020-03-26T23:36:04+08:00","userid":2} {"level":"info","msg":"login","time":"2020-03-26T23:36:04+08:00","userid":3} {"level":"info","msg":"logoff","time":"2020-03-26T23:36:04+08:00","userid":3} {"level":"info","msg":"login","time":"2020-03-26T23:36:04+08:00","userid":4} {"level":"info","msg":"logoff","time":"2020-03-26T23:36:04+08:00","userid":4} {"level":"info","msg":"login","time":"2020-03-26T23:36:04+08:00","userid":5} {"level":"info","msg":"logoff","time":"2020-03-26T23:36:04+08:00","userid":5} {"level":"info","msg":"login","time":"2020-03-26T23:36:04+08:00","userid":6} {"level":"info","msg":"logoff","time":"2020-03-26T23:36:04+08:00","userid":6} {"level":"info","msg":"login","time":"2020-03-26T23:36:04+08:00","userid":7} {"level":"info","msg":"logoff","time":"2020-03-26T23:36:04+08:00","userid":7} {"level":"info","msg":"login","time":"2020-03-26T23:36:04+08:00","userid":8} {"level":"info","msg":"logoff","time":"2020-03-26T23:36:04+08:00","userid":8} {"level":"info","msg":"login","time":"2020-03-26T23:36:04+08:00","userid":9} {"level":"info","msg":"logoff","time":"2020-03-26T23:36:04+08:00","userid":9} {"level":"info","msg":"login","time":"2020-03-26T23:36:04+08:00","userid":10} {"level":"info","msg":"logoff","time":"2020-03-26T23:36:04+08:00","userid":10}
因为每一行都是一个单独的 JSON 串,咱们可使用jj
支持的 JSON 行特性,使用..
路径标识这些行。..
使得jj
将这些行当作数组的元素。咱们能够读取这些数组元素。
获取数组长度,返回 20:
$ jj -i log.txt ..# 20
只读取每一行中的userid
信息:
$ jj -i log.txt ..#.userid [1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10]
只读取每一行中的msg
信息:
$ jj -i log.txt ..#.msg ["login","logoff","login","logoff","login","logoff","login","logoff","login","logoff","login","logoff","login","logoff","login","logoff","login","logoff","login","logoff"]
更复杂一点的,若是咱们要查看全部userid=1
的日志:
$ jj -i log.txt ..#\(userid=1\)# -p [ { "level": "info", "msg": "login", "time": "2020-03-26T23:36:04+08:00", "userid": 1 }, { "level": "info", "msg": "logoff", "time": "2020-03-26T23:36:04+08:00", "userid": 1 } ]
上面的命令注意两点,(
和)
是 shell 中的特殊字符,须要\
转义。命令中咱们使用-p
选项使结果更易读。
若是咱们只须要查找第一条符合条件的日志,则能够去掉最右侧的#
:
$ jj -i log.txt ..#\(userid=1\) -p { "level": "info", "msg": "login", "time": "2020-03-26T23:36:04+08:00", "userid": 1 }
若是要查看全部的登陆信息:
$ jj -i log.txt ..#\(msg="login"\)# -p [ { "level": "info", "msg": "login", "time": "2020-03-26T23:36:04+08:00", "userid": 1 }, { "level": "info", "msg": "login", "time": "2020-03-26T23:36:04+08:00", "userid": 2 }, { "level": "info", "msg": "login", "time": "2020-03-26T23:36:04+08:00", "userid": 3 }, { "level": "info", "msg": "login", "time": "2020-03-26T23:36:04+08:00", "userid": 4 }, { "level": "info", "msg": "login", "time": "2020-03-26T23:36:04+08:00", "userid": 5 }, { "level": "info", "msg": "login", "time": "2020-03-26T23:36:04+08:00", "userid": 6 }, { "level": "info", "msg": "login", "time": "2020-03-26T23:36:04+08:00", "userid": 7 }, { "level": "info", "msg": "login", "time": "2020-03-26T23:36:04+08:00", "userid": 8 }, { "level": "info", "msg": "login", "time": "2020-03-26T23:36:04+08:00", "userid": 9 }, { "level": "info", "msg": "login", "time": "2020-03-26T23:36:04+08:00", "userid": 10 } ]
jj
是一个很是使用的 JSON 命令行工具,性能超赞。执行jj -h
去看看其余选项吧。
你们若是发现好玩、好用的 Go 语言库,欢迎到 Go 每日一库 GitHub 上提交 issue😄
欢迎关注个人微信公众号【GoUpUp】,共同窗习,一块儿进步~