json 如何使用jq更新一个带有md5的对象中的值?

h9a6wy2h  于 11个月前  发布在  其他
关注(0)|答案(2)|浏览(154)

假设我有以下JSON文档(受this post启发)

初始文件

{
  "key": "value",
  "ips": [
    {
      "ip": "1.2.3.4",
      "macAddress": "ac:5f:3e:87:d7:1a"
    },
    {
      "ip": "5.6.7.8",
      "macAddress": "ac:5f:3e:87:d7:2a"
    },
    {
      "ip": "9.10.11.12",
      "macAddress": "ac:5f:3e:87:d7:3a"
    },
    {
      "ip": "13.14.15.16",
      "macAddress": "42:12:20:2e:2b:ca"
    }
  ]
}

字符串
现在我想读取每个macAddress,将其传递给哈希函数(例如md5sum),并将结果写回JSON文档。

期望输出

{
  "key": "value",
  "ips": [
    {
      "ip": "1.2.3.4",
      "macAddress": "45ee585278a0717c642ff2cb25a8e441"
    },
    {
      "ip": "5.6.7.8",
      "macAddress": "ab47bf90cb9f385127977569e676ce70"
    },
    {
      "ip": "9.10.11.12",
      "macAddress": "a5e9785db428e3956a47776dbd00fc91"
    },
    {
      "ip": "13.14.15.16",
      "macAddress": "f75d61937f70252ff139adee241daab4"
    }
  ]
}


目前我有下面的shell脚本,但我认为它可以做得更优雅......最好在一行程序。

json_doc="{\"key\": \"value\", \"ips\": [{\"ip\":\"1.2.3.4\",\"macAddress\":\"ac:5f:3e:87:d7:1a\"},{\"ip\":\"5.6.7.8\",\"macAddress\":\"ac:5f:3e:87:d7:2a\"},{\"ip\":\"9.10.11.12\",\"macAddress\":\"ac:5f:3e:87:d7:3a\"},{\"ip\":\"13.14.15.16\",\"macAddress\":\"42:12:20:2e:2b:ca\"}]}"

ip_list=$(jq -c '.ips[]' <<< "$json_doc" |
while read -r jsonline ; do
  hashmac="$(jq -s -j '.[] | .macAddress' <<<"$jsonline" | md5sum | cut -d ' ' -f1)"
  jq --arg hashmac "$hashmac" -s -r '.[] | .macAddress |= "\($hashmac)"' <<<"$jsonline"
done | jq -s)

# Update json document with ip list containing hashed mac addresses
jq --argjson ips "$ip_list" '.ips = $ips' <<<"$json_doc"

irtuqstp

irtuqstp1#

peak的answer是链接问题的变体。jq的两次调用,首先用于计算md5哈希值,然后使用reduce将计算结果重新构造回原始JSON

jq -r '.ips[].macAddress' input.json |
while read -r line ; do printf '%s' "$line" | md5 ; done |
jq -s -R --slurpfile json input.json 'split("\n")
  | map(select(length>0))
  | . as $in
  | reduce range(0;length) as $i ($json; .[].ips[$i].macAddress = $in[$i])'

字符串
第二个jq调用应该仔细阅读。初始参数-s -R用于将for循环创建的多行非JSON输出阅读到jq的上下文中。而--slurpfile参数用于将计算的哈希更新回原始JSON。slurp操作将整个文件带入内存。
因此,这个命令对于非常大的JSON文件可能无效。

46qrfjad

46qrfjad2#

另一种方法是使用jq将JSON分解为标量行,然后过滤并处理jq之外的相关行,并最终通过第二次调用jq重新组装该流。
这里有一个使用jq的流表示的例子,即jq -c . --stream用于分解,jq -n 'fromstream(inputs)'用于重组,awk用于实际处理,因为它可以很容易地按行读取和过滤,改变它的一部分,和shell来执行外部任务。要过滤像[["ips",0,"macAddress"],"ac:5f:3e:87:d7:1a"]这样的行,同时通过像[["ips",0,"ip"],"1.2.3.4"][["ips",0,"macAddress"]]这样的行,一种简单的方法是将每行解释为用双引号"分隔的列,然后过滤匹配给定内容的第2和第4列,第6列不为空。(鲁棒性可明显提高;这只是一个例子),然后替换第6列(使用getline),printf %s的输出是第6列的值,然后是您的md5sumcut处理。(使用onetrueawk/awk版本20231124和GNU Awk 5.3.0进行了测试。)

jq -c . --stream input.json | awk '
  BEGIN { FS = OFS = "\"" }
  $2 == "ips" && $4 == "macAddress" && $6 {
    "printf %s " $6 " | md5sum | cut -d \\  -f1" | getline $6
  }
  1
' | jq -n 'fromstream(inputs)'

字符串
下面是另一个更健壮的示例,它“手动”将输入分解为值和路径,同时还附加了一个标志来标记需要进一步处理的标量(通过.ips[].macAddress路径表达式jq查询),转换成像"1.2.3.4" ["ips",0,"ip"] false"ac:5f:3e:87:d7:1a" ["ips",0,"macAddress"] true这样的行。这个例子的处理部分只使用POSIX-兼容的shell特性,如read,通过行来进行重定向,一个case语句,基于该标志进行偏转,和tr以及参数扩展来提取IP最后的jq composer然后使用reduce收集行,并使用setpath连续构建输出。

jq -r '
  [ path(.ips[].macAddress) ] as $q
  | paths(type | IN("object","array") | not) as $p
  | @json "\(getpath($p)) \($p) \(IN($p;$q[]))"
' input.json |

while read -r line; do case "$line" in
  *false) printf '%s\n' "$line" ;;
  *true) printf '"%s" %s\n' "$(printf '%s' "${line%% *}" | tr -d '"' |
         md5sum | cut -d ' ' -f1)" "${line#* }" ;;
esac; done |

jq -s 'reduce _nwise(3) as [$v,$p] (null; setpath($p;$v))'


对于给定的输入,两个示例都输出

{
  "key": "value",
  "ips": [
    {
      "ip": "1.2.3.4",
      "macAddress": "45ee585278a0717c642ff2cb25a8e441"
    },
    {
      "ip": "5.6.7.8",
      "macAddress": "ab47bf90cb9f385127977569e676ce70"
    },
    {
      "ip": "9.10.11.12",
      "macAddress": "a5e9785db428e3956a47776dbd00fc91"
    },
    {
      "ip": "13.14.15.16",
      "macAddress": "f75d61937f70252ff139adee241daab4"
    }
  ]
}

相关问题