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

mic*_*_65 19 bash json 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
Run Code Online (Sandbox Code Playgroud)

输出来自docker stats命令,我的最终目标是将自定义指标发布到aws cloudwatch.我想将此字符串格式化为json.

{
    "CONTAINER":"nginx_container",
    "CPU%":"0.02%", 
    ....
}
Run Code Online (Sandbox Code Playgroud)

我之前使用过jq命令,看起来它应该在这种情况下运行良好,但我还没有找到一个好的解决方案.除了硬编码变量名和使用sed或awk索引.然后从头开始创建一个json.任何建议,将不胜感激.谢谢.

Cha*_*ffy 42

条件

对于以下所有内容,假设您的内容位于名为的shell变量中s:

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'
Run Code Online (Sandbox Code Playgroud)

什么(现代jq)

# thanks to @JeffMercado and @chepner for refinements, see comments
jq -Rn '
( input  | split("|") ) as $keys |
( inputs | split("|") ) as $vals |
[[$keys, $vals] | transpose[] | {key:.[0],value:.[1]}] | from_entries
' <<<"$s"
Run Code Online (Sandbox Code Playgroud)

怎么样(现代jq)

这需要非常新的(可能是1.5?)jq才能工作,并且是一大堆密码.要打破它:

  • 使用-n防止jq从读取自身标准输入,而使输入流提供给由被读取的全部inputinputs前者读取单个线,后者读取所有剩余的行- .(-R对于原始输入,会导致文本行而不是JSON对象被读取).
  • 有了[$keys, $vals] | transpose[],我们生成[key, value]对(用Python术语,压缩两个列表).
  • 有了{key:.[0],value:.[1]},我们将每[key, value]对成为表格的对象{"key": key, "value": value}
  • 有了from_entries,我们将这些对组合成包含这些键和值的对象.

什么(壳辅助)

这将使用jq比上面更大的版本,并且对于本机jq解决方案可能更难以纠结的场景,这是一种容易采用的方法:

{
   IFS='|' read -r -a keys # read first line into an array of strings

   ## read each subsequent line into an array named "values"
   while IFS='|' read -r -a values; do

    # setup: positional arguments to pass in literal variables, query with code    
    jq_args=( )
    jq_query='.'

    # copy values into the arguments, reference them from the generated code    
    for idx in "${!values[@]}"; do
        [[ ${keys[$idx]} ]] || continue # skip values with no corresponding key
        jq_args+=( --arg "key$idx"   "${keys[$idx]}"   )
        jq_args+=( --arg "value$idx" "${values[$idx]}" )
        jq_query+=" | .[\$key${idx}]=\$value${idx}"
    done

    # run the generated command
    jq "${jq_args[@]}" "$jq_query" <<<'{}'
  done
} <<<"$s"
Run Code Online (Sandbox Code Playgroud)

如何(贝壳辅助)

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' \
   <<<'{}'
Run Code Online (Sandbox Code Playgroud)

...将每个键和值传递到带外(这样它被视为文字字符串而不是解析为JSON),然后单独引用它们.


结果

上述任何一种都会发出:

{
  "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"
}
Run Code Online (Sandbox Code Playgroud)

为什么

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

考虑以下作为一个破坏更天真的方法的例子:

s='key ending in a backslash\
value "with quotes"'
Run Code Online (Sandbox Code Playgroud)

当然,这些是意想不到的情况,但jq知道如何处理它们:

{
  "key ending in a backslash\\": "value \"with quotes\""
}
Run Code Online (Sandbox Code Playgroud)

...而不理解JSON字符串的实现很容易最终发出:

{
  "key ending in a backslash\": "value "with quotes""
}
Run Code Online (Sandbox Code Playgroud)

  • 我不知道是否有一种惯用的方法可以做到这一点,但我看到它可以通过多种方式实现.我个人喜欢使用`from_entries`:`[[$ keys,$ values] | 转置[] | {key:.[0],value:.[1]}] | from_entries`.或者从对中创建对象并添加它们:`[[$ keys,$ values] | 转置[] | {(.[0]):.[1]}] | add`.或者使用`reduce`来赋值:`reduce([$ keys,$ values] | transpose [])为$ p({};.[$ p [0]] = $ p [1])` (2认同)
  • 这似乎有效:`jq -Rn'(输入| split("|"))为$ keys | (输入| split("|"))为$ vals | [[$ keys,$ vals] | transpose [] ...`.我没有看到完全避免变量的方法,可能是由于评估过滤器的顺序. (2认同)

Jim*_*Jim 13

我知道这是一篇旧帖子,但您寻求的工具称为johttps : //github.com/jpmens/jo

一个快速简单的例子:

$ jo my_variable="simple"
{"my_variable":"simple"}
Run Code Online (Sandbox Code Playgroud)

稍微复杂一点

$ jo -p name=jo n=17 parser=false
{
  "name": "jo",
  "n": 17,
  "parser": false
}
Run Code Online (Sandbox Code Playgroud)

添加数组

$ 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
  ]
}
Run Code Online (Sandbox Code Playgroud)

我用 jo 制作了一些非常复杂的东西,好处是你不必担心滚动你自己的解决方案,担心制作无效的 json 的可能性。


Mat*_*ice 8

你可以先让docker给你JSON数据

docker stats --format "{{json .}}"
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参阅:https : //docs.docker.com/config/formatting/