在Bash脚本中用jq构建多层JSON:动态指定数组索引问题
问题:Bash中使用jq动态指定数组索引构建多层JSON
我在Bash脚本中读取文本文件内容到数组,遍历元素尝试构建多层JSON,脚本如下:
#!/bin/bash JSON=$(jq -n '') readarray -t array < ./station.sample count=0 for e in "${array[@]}" do if echo "$e" | grep -Eq '^[0-9]{10}' >/dev/null then timestamp=$e datetime=`date +'%Y-%m-%d %H:%M:%S' -d "@$e"` JSON=$(echo $JSON | jq --arg timestamp "${timestamp}" '. += $ARGS.named') JSON=$(echo $JSON | jq --arg datetime "${datetime}" '. += $ARGS.named') fi if echo "$e" | grep '^Station ' >/dev/null then NODE=$(jq -n '') mac=`echo "$e" | awk '{ print $2 }'` interface=`echo "$e" | awk '{ print $4 }' | rev | cut -c2- | rev` JSON=$(echo $JSON | jq --argjson nodes "[]" '. += $ARGS.named') JSON=$(echo $JSON | jq --arg mac "${mac}" --arg count "${count}" '.nodes[0] += {"mac": $mac}') JSON=$(echo $JSON | jq --arg interface "${interface}" '.nodes[0] += {"interface": $interface}') count=$((count+1)) fi done
执行后得到的JSON如下:
{ "timestamp": "1721396365", "datetime": "2024-07-19 16:39:25", "nodes": [ { "mac": "14:88:00:00:00:06" } ] }
我需要向nodes列表中添加多个对象,因此设置了count变量尝试指定数组索引:
JSON=$(echo $JSON | jq --arg mac "${mac}" --arg count "${count}" '.nodes[$count] += {"mac": $mac}')
但这种方式无法添加列表项,我还尝试过以下方案也未解决问题:
NODE=$(echo $NODE | jq --arg mac "$mac" '. += $ARGS.named') NODE=$(echo $NODE | jq --arg interface "${interface}" '. += $ARGS.named') JSON=$(echo $JSON | jq --argjson node "${NODE}" '.nodes[.nodes | length] = $ARGS.named')
要求必须使用变量来指定列表的索引。
解决方案
核心问题分析
- 变量类型不匹配:用
--arg传入jq的count是字符串类型,而jq数组索引需要数字,直接使用会导致索引无效。 - 数组初始化逻辑错误:原脚本每次遇到
Station行都会重新将nodes初始化为空数组,覆盖之前已添加的节点。
修改后的关键代码
针对Station行的处理逻辑修改如下:
if echo "$e" | grep '^Station ' >/dev/null then mac=$(echo "$e" | awk '{ print $2 }') interface=$(echo "$e" | awk '{ print $4 }' | rev | cut -c2- | rev) # 确保nodes数组已存在,不存在则初始化 JSON=$(echo "$JSON" | jq 'if .nodes then . else .nodes = [] end') # 将count字符串转为数字后作为索引,添加/更新节点 JSON=$(echo "$JSON" | jq --arg mac "$mac" --arg interface "$interface" --arg count "$count" \ '.nodes[$count | tonumber] = {"mac": $mac, "interface": $interface}') count=$((count+1)) fi
完整修改后的脚本
#!/bin/bash JSON=$(jq -n '') readarray -t array < ./station.sample count=0 for e in "${array[@]}" do if echo "$e" | grep -Eq '^[0-9]{10}' >/dev/null then timestamp=$e datetime=$(date +'%Y-%m-%d %H:%M:%S' -d "@$e") JSON=$(echo "$JSON" | jq --arg timestamp "$timestamp" --arg datetime "$datetime" '. += $ARGS.named') fi if echo "$e" | grep '^Station ' >/dev/null then mac=$(echo "$e" | awk '{ print $2 }') interface=$(echo "$e" | awk '{ print $4 }' | rev | cut -c2- | rev) JSON=$(echo "$JSON" | jq 'if .nodes then . else .nodes = [] end') JSON=$(echo "$JSON" | jq --arg mac "$mac" --arg interface "$interface" --arg count "$count" \ '.nodes[$count | tonumber] = {"mac": $mac, "interface": $interface}') count=$((count+1)) fi done # 输出最终JSON echo "$JSON"
说明
- 使用
$count | tonumber将字符串类型的count转为数字,满足jq数组索引的类型要求。 - 先判断
nodes是否存在,不存在才初始化,避免覆盖已有数据。 - 合并了节点属性的赋值操作,减少jq调用次数,提升效率。
内容的提问来源于stack exchange,提问作者AndAsh AndAsh
相关产品推荐
相关产品推荐

