首页 > 其他分享 >Go每日一库之26:jj

Go每日一库之26:jj

时间:2023-09-27 18:58:02浏览次数:41  
标签:26 level userid 00 一库 2020 jj Go msg

简介

在前面两篇文章中,我们分别介绍了快速读取 JSON 值的库gjson和快速设置 JSON 值的库sjson。今天我们介绍它们的作者tidwall的一个基于gjsonsjson的非常实用的命令行工具jj。它是使用 Go 编写的快速读取和设置 JSON 值的命令行程序。

快速使用

Mac 上可以直接使用brew install tidwall/jj/jj安装。其他系统可以通过下载编译好的可执行程序,下载地址为https://github.com/tidwall/jj/releases

我选择使用go get安装:

$ go get github.com/tidwall/jj/cmd/jj

上面命令执行完成之后,编译生成的jj程序会放在$GOPATH/bin目录中,我习惯把$GOPATH/bin加入系统可执行目录$PATH中,故可以直接使用。

简单的读取和设置(我的环境为 Win10 + Git Bash):

$ 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 串。键路径在前两篇文章中有详细的介绍,不熟悉的可以回去看一下。

读取和设置

实际上读取和设置的语法和形式与我们前面介绍gjsonsjson提到的基本一样,只不过是在命令行上完成的而已。

读取不存在的字段,返回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

标签:26,level,userid,00,一库,2020,jj,Go,msg
From: https://www.cnblogs.com/arena/p/17733427.html

相关文章

  • mongo数据库学习
               实现了数据库的增删改查......
  • golang-waitgroup
    说明golang通过waitgroup来实现并发控制,用法跟java的CountDownLatch 效果一样 WaitGroup的使用场景和方法我们通过goroutine运行一个或者一组任务,需要关心这组任务执行完了进行通知WaitGroup如同它的字面意思,就是等待一组goroutine运行完成,主要有三个方法组成:Add(de......
  • 1.go安装和包管理工具
    下载地址安装之后,自动添加系统环境变量PATH=>"C:\Go\bin"查看go版本信息goenv使用七牛云设置golang环境,代理goenv-wGO111MODULE=ongoenv-wGOPROXY=https://goproxy.cn,direct保管理工具go1.11之后,使用goMod管理go初始化gomodeinitnameuser......
  • fmt 库简介和示例【GO 基础】
    〇、关于fmtfmt标准库是Go语言标准库的一部分,提供了格式化字符串、输入输出等基本功能。通过fmt库,我们可以进行简单的格式化输出、读取用户输入、错误输出等操作。fmt库实现了类似C语言printf和scanf的格式化I/O,主要分为向外输出内容和获取输入内容两大部分,本文将......
  • Go - receiving from an empty channel
     packagemainimport("fmt""sync""time")varworkers=3funcprocessItem(input<-chanint,outputchan<-int,wg*sync.WaitGroup){for{fmt.Println("=")in:=<-i......
  • P3147 [USACO16OPEN] 262144 P
    Link这个题有一个很特殊的点,就是最大值不会超过28,可以想一下最多可以合并多少次。那么常规的区间dp是不能使用的,就要采用特殊的形式,因为很难的确定应该怎么转移,那么就换一种思路,转移的对象变成另外一个端点。\(dp_{i,j}\)表示\(i\)在左边,达到\(j\)的话的右端点位置\(dp_{i,j......
  • Go - ERROR: fatal error: all goroutines are asleep - deadlock!
    main.go:packagemainimport"fmt"funcmain(){ch:=make(chanint)ch<-1a:=<-chfmt.Println(a)}Goterror:zzh@ZZHPC:/zdata/MyPrograms/Go/testing$gorunmain.gofatalerror:allgoroutinesareasleep-deadlo......
  • Go - Live reload of configurations
    main.go:packagemainimport("encoding/json""fmt""log""os""time""github.com/fsnotify/fsnotify")typeconfigstruct{Namestring`json:"name"`}......
  • 一个思路:实现 golang 中的 `__file__` `__line__` 宏
    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!cnblogs博客zhihuGithub公众号:一本正经的瞎扯测试zaplog发现,开启caller的调用,会使整个服务增加2%的损耗。其实文件及其行号完全可以在编译期加上去,没必要带来运行期的性能损耗。因此有一个思路,可以解决这......
  • 2023-09-20:用go语言,保证一定是n*n的正方形,实现从里到外转圈打印的功能 如果n是奇数,中
    2023-09-20:用go语言,保证一定是n*n的正方形,实现从里到外转圈打印的功能如果n是奇数,中心点唯一,比如abcdefghie是中心点,依次打印:efihgdabc如果n是偶数,中心点为最里层2*2的右下点比如abcdefghijklmnopqrstuvwxyz0123456789最里层是opu......