在bash中使用jq从管道分隔的键和值创建JSON
我试图从bash中的字符串创建一个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
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
]
}