You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

jq结合shell遍历JSON键时如何在循环内获取嵌套属性值

问题背景

你有一份存储EC2实例元数据的JSON文件instances_metadata.json,结构如下:

{
  "instance-01": {
    "instance_aws_az": "eu-west-1a",
    "instance_aws_dns_name": "<ip>eu-west-1.compute.amazonaws.com",
    "instance_ec2_tags": {
      "Name": "2ed97a79c27be6bca883",
      "Serial_number": "22000528"
    },
    "instance_sg_groups": []
  },
  "instance-29": {
    "instance_aws_az": "eu-west-1a",
    "instance_aws_dns_name": "<ip>eu-west-1.compute.amazonaws.com",
    "instance_ec2_tags": {
      "Name": "2ed97a79c27be6bca773",
      "Serial_number": "22000578"
    },
    "instance_sg_groups": []
  }
}

需求为遍历文件中所有顶层实例ID(如instance-01、instance-29),在shell循环中读取每个实例对应的嵌套属性(比如标签里的Serial_number),针对指定实例执行aws s3 cp等自定义操作。原有脚本存在逻辑错误,无法正确读取对应实例的字段值。

原脚本问题说明
  • Shell不会自动解析JSON路径,直接写$i.instance_ec2_tags.Serial_number只是普通字符串拼接,根本无法获取JSON内的字段值
  • 循环内的jq查询逻辑完全写反:.[].instance_ec2_tags.Serial_number | select(. == "instance-01")是筛选序列号等于instance-01的条目,而非查询键名为instance-01的实例对应的序列号
  • 逐次读取键再反复调用jq扫描整个文件,会产生不必要的IO开销,执行效率低
  • 直接在jq表达式中拼接shell变量存在特殊字符转义、代码注入的风险

注:你最初提供的示例JSON存在不符合规范的尾逗号(对象最后一个属性后多余的逗号),如果执行jq时报解析错误,先删掉属性行末多余的逗号即可。

推荐实现方案

优先采用单次jq调用输出所有所需字段、再由shell循环读取的方案,执行效率最高,逻辑也最清晰:

#!/bin/bash
# 一次性提取所有实例的需要字段,用制表符分隔避免字段含空格导致解析错误
jq -r 'to_entries[] | "\(.key)\t\(.value.instance_ec2_tags.Serial_number)\t\(.value.instance_aws_az)\t\(.value.instance_aws_dns_name)"' instances_metadata.json | while IFS=$'\t' read -r instance_id serial az dns_name; do
    echo "Instance $instance_id: Serial Number is: $serial"
    echo "  Availability Zone: $az"
    echo "  DNS Name: $dns_name"

    # 针对指定实例执行自定义逻辑
    if [ "$instance_id" = "instance-01" ]; then
        echo "Start processing target instance $instance_id, serial number: $serial"
        # 在此处补充aws s3 cp等操作,直接调用$serial等变量即可
        # 例:aws s3 cp ./config.yaml s3://your-bucket/instance-config/$serial/config.yaml
    fi
done

方案原理

  • 借助jq的to_entries函数,将原始顶层对象转换为[{"key":"实例ID", "value":{实例属性对象}}]的结构化数组,遍历过程中可以同时拿到顶层键名和对应的所有嵌套属性
  • 所有字段一次性提取完成,全程只调用一次jq,不需要循环内重复扫描文件
  • 用制表符作为字段分隔符,避免字段值包含空格时出现read拆分错误

如果需要保留先遍历键名、再按需查询字段的逻辑,可以修正jq传参方式实现:

jq -r 'keys[]' instances_metadata.json | while read -r instance_id; do
    # 通过--arg参数将shell变量安全传入jq,查询当前实例ID对应的序列号
    serial_number=$(jq -r --arg inst "$instance_id" '.[$inst].instance_ec2_tags.Serial_number' instances_metadata.json)
    echo "Instance $instance_id: Serial Number is: $serial_number"

    if [ "$instance_id" = "instance-01" ]; then
        echo "Processing target instance, serial: $serial_number"
        # 补充自定义操作
    fi
done

注意:不要直接在jq的过滤表达式里拼接shell变量,必须使用--arg参数传值,避免特殊字符导致的语法错误和安全问题。

内容的提问来源于stack exchange,提问作者VCM

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 15:24:34