在bash中使用jq从管道分隔的键和值创建JSON

在bash中使用jq从管道分隔的键和值创建JSON,json,bash,docker,jq,Json,Bash,Docker,Jq,我试图从bash中的字符串创建一个json对象。字符串如下所示 CONTAINER|CPU%|MEMUSAGE/LIMIT|MEM%|NETI/O|BLOCKI/O|PIDS nginx_container|0.02%|25.09MiB/15.26GiB|0.16%|0B/0B|22.09MB/4.096kB|0 输出来自docker stats命令,我的最终目标是将自定义度量发布到aws cloudwatch。我想将这个字符串格式化为json { "CONTAINER":"nginx

我试图从bash中的字符串创建一个json对象。字符串如下所示

CONTAINER|CPU%|MEMUSAGE/LIMIT|MEM%|NETI/O|BLOCKI/O|PIDS
nginx_container|0.02%|25.09MiB/15.26GiB|0.16%|0B/0B|22.09MB/4.096kB|0
输出来自docker stats命令,我的最终目标是将自定义度量发布到aws cloudwatch。我想将这个字符串格式化为json

{
    "CONTAINER":"nginx_container",
    "CPU%":"0.02%", 
    ....
}
我以前使用过jq命令,在这种情况下,它似乎应该工作得很好,但我还没有找到一个好的解决方案。除了使用sed或awk硬编码变量名和索引之外。然后从头开始创建json。如有任何建议,将不胜感激。谢谢

JSONSTR=""
declare -a JSONNAMES=()
declare -A JSONARRAY=()
LOOPNUM=0

cat ~/newfile | while IFS=: read CONTAINER CPU MEMUSE MEMPC NETIO BLKIO PIDS; do
    if [[ "$LOOPNUM" = 0 ]]; then
        JSONNAMES=("$CONTAINER" "$CPU" "$MEMUSE" "$MEMPC" "$NETIO" "$BLKIO" "$PIDS")
        LOOPNUM=$(( LOOPNUM+1 ))
    else
        echo "{ \"${JSONNAMES[0]}\": \"${CONTAINER}\", \"${JSONNAMES[1]}\": \"${CPU}\", \"${JSONNAMES[2]}\": \"${MEMUSE}\", \"${JSONNAMES[3]}\": \"${MEMPC}\", \"${JSONNAMES[4]}\": \"${NETIO}\", \"${JSONNAMES[5]}\": \"${BLKIO}\", \"${JSONNAMES[6]}\": \"${PIDS}\" }"
    fi 
done
返回:

{ "CONTAINER": "nginx_container", "CPU%": "0.02%", "MEMUSAGE/LIMIT": "25.09MiB/15.26GiB", "MEM%": "0.16%", "NETI/O": "0B/0B", "BLOCKI/O": "22.09MB/4.096kB", "PIDS": "0" }
先决条件 对于以下所有内容,都假定您的内容位于名为
s
的shell变量中:

s='CONTAINER|CPU%|MEMUSAGE/LIMIT|MEM%|NETI/O|BLOCKI/O|PIDS
nginx_container|0.02%|25.09MiB/15.26GiB|0.16%|0B/0B|22.09MB/4.096kB|0'
什么(现代jq)
如何(壳牌协助) 从上面调用的
jq
命令类似于:

jq --arg key0   'CONTAINER' \
   --arg value0 'nginx_container' \
   --arg key1   'CPU%' \
   --arg value1 '0.0.2%' \
   --arg key2   'MEMUSAGE/LIMIT' \
   --arg value2 '25.09MiB/15.26GiB' \
   '. | .[$key0]=$value0 | .[$key1]=$value1 | .[$key2]=$value2' \
   <<<'{}'

为什么? 简而言之:因为它保证生成有效的JSON作为输出

以以下为例,可以打破更幼稚的方法:

s='key ending in a backslash\
value "with quotes"'
当然,这些都是意外的情况,但jq知道如何处理它们:

{
  "key ending in a backslash\\": "value \"with quotes\""
}
…而不理解JSON字符串的实现可能很容易发出:

{
  "key ending in a backslash\": "value "with quotes""
}

下面是一个使用
-R
-s
选项以及以下选项的解决方案:

json_template='{“容器”:“%s”,“CPU%”:“%s”,“内存使用/限制”:“%s”,“内存%”:“%s”,“NETI/O”:“%s”,“块I/O”:“%s”,“PIDS”:“%s”}
json_字符串=$(printf“$json_模板”“nginx_容器”“0.02%”“25.09MiB/15.26GiB”“0.16%”“0B/0B”“22.09MB/4.096kB”“0”)
回显“$json_字符串”

不使用jq,但可以在值中使用args和environment

CONTAINER=nginx\u CONTAINER
json_template='{“容器”:“%s”、“CPU%”::“%s”、“内存使用/限制”:“%s”、“内存%:“%s”、“NETI/O”:“%s”、“块I/O”:“%s”、“PIDS”:“%s”}
json_字符串=$(printf“$json_模板”“$CONTAINER”“$1”“25.09MiB/15.26GiB”“0.16%”“0B/0B”“22.09MB/4.096kB”“0”)

echo“$json_string”

如果您是从表格数据开始的,我认为使用本机可以处理表格数据的东西更有意义,比如将其转换为json,然后使用jq进一步处理它

echo 'CONTAINER|CPU%|MEMUSAGE/LIMIT|MEM%|NETI/O|BLOCKI/O|PIDS
nginx_container|0.02%|25.09MiB/15.26GiB|0.16%|0B/0B|22.09MB/4.096kB|0' \
        | sqawk -FS '[|]' -RS '\n' -output json 'select * from a' header=1 \
        | jq '.[] | with_entries(select(.key|test("^a.*")|not))'

    {
      "CONTAINER": "nginx_container",
      "CPU%": "0.02%",
      "MEMUSAGE/LIMIT": "25.09MiB/15.26GiB",
      "MEM%": "0.16%",
      "NETI/O": "0B/0B",
      "BLOCKI/O": "22.09MB/4.096kB",
      "PIDS": "0"
    }
如果没有
jq
sqawk
给出的信息有点太多:

[
  {
    "anr": "1",
    "anf": "7",
    "a0": "nginx_container|0.02%|25.09MiB/15.26GiB|0.16%|0B/0B|22.09MB/4.096kB|0",
    "CONTAINER": "nginx_container",
    "CPU%": "0.02%",
    "MEMUSAGE/LIMIT": "25.09MiB/15.26GiB",
    "MEM%": "0.16%",
    "NETI/O": "0B/0B",
    "BLOCKI/O": "22.09MB/4.096kB",
    "PIDS": "0",
    "a8": "",
    "a9": "",
    "a10": ""
  }
]

您可以让docker首先为您提供JSON数据

docker stats --format "{{json .}}"

有关这方面的更多信息,请参阅:

我知道这是一篇旧文章,但您寻找的工具名为
jo

一个简单的例子:

$ jo my_variable="simple"
{"my_variable":"simple"}
稍微复杂一点

$ jo -p name=jo n=17 parser=false
{
  "name": "jo",
  "n": 17,
  "parser": false
}
添加一个数组

$ jo -p name=jo n=17 parser=false my_array=$(jo -a {1..5})
{
  "name": "jo",
  "n": 17,
  "parser": false,
  "my_array": [
    1,
    2,
    3,
    4,
    5
  ]
}

我用jo做了一些非常复杂的事情,好的是你不必担心自己的解决方案会产生无效的json。

我不认为JQ是这个工作的工具(它是json in/json out)。我最近做了一些类似的事情,最后使用了RUBY CSV和json模块(CSV可以使用|作为分隔符)Python有类似的类有些人使用awk从分隔符创建JSONinput@Jimmy,嗯?jq绝对是这项工作的优秀工具。@Jimmy,…jq不限于JSON。它可以读取原始字符串(请参阅
-R
选项),并支持正则表达式(因此它可以解析您认为适合发送它的任何语法).@Jimmy,…也不限于JSON输出;当前版本还支持编写CSV、%编码的URI、HTML、POSIX sh兼容的shell转义语法和base64编码的文本字符串。仅供参考——请参阅的第四段,指定环境变量名称的约定:OS和shell使用名称使用大写字符仅限Acter,而小写名称是“为应用程序保留的”,并保证应用程序可以在该空间中定义任何名称,而无需修改标准的实用程序行为。此约定也会影响外壳变量,因为它们共享一个命名空间:将环境变量的名称用于外壳变量会覆盖该环境变量,从而导致冲突。我将分开读取标题从循环中删除,这样您就可以避免
LOOPNUM
逻辑:
。{IFS=:read-ajsonnames;而IFS=:read…;do echo…;done;}
。顺便说一句,进入循环意味着您无法保留超过该循环出口的状态[缺少带有
lastpipe
选项的全新bash,或者缺少默认行为(如
ksh
)的shell。请参阅BashFAQ#24()--
while…;done如果您使用
transpose
会更容易些,因为您已经有了键和值的数组。transpose有效地将它们拉到一起,这将使您可以更轻松地构建对象。@JeffMercado,我觉得我遗漏了一些显而易见的东西——有没有一个比<代码>[$keys,$VAL]|转置|[.]{“键”:[0],“值”:[1]}]|从|项转置
?我不知道是否有一种惯用的方法可以做到这一点,但我看到有很多方法可以实现。我个人喜欢使用从|项转置[$keys,$values]|转置{key:[0],value:[1]]|从_条目。或从对中创建对象并将它们相加:
[[$keys,$values]|转置[]{([0]):[1]}]|添加
。或使用
reduce
分配值:
reduce([$keys,$values]|转置[])为$p({};[$p[0]=$p[1])
Oooh--
add
的用法是我不知道的一个技巧。闪亮!:)这似乎有效:
jq-Rn'(input | split(|)作为$keys |(input | split(|)作为$vals |[[$keys,$vals]| transpose[]…
。我还没有找到一种完全避免变量的方法,可能是因为过滤器的计算顺序。这是一个创建JSON文件的非常简单的工具,比
jq
的开销要少。遗憾的是,Amazon Linux上没有
jo
docker stats --format "{{json .}}"
$ jo my_variable="simple"
{"my_variable":"simple"}
$ jo -p name=jo n=17 parser=false
{
  "name": "jo",
  "n": 17,
  "parser": false
}
$ jo -p name=jo n=17 parser=false my_array=$(jo -a {1..5})
{
  "name": "jo",
  "n": 17,
  "parser": false,
  "my_array": [
    1,
    2,
    3,
    4,
    5
  ]
}