180

我在 shell 脚本中使用jq工具(jq-json-processor)来解析 json。

我有 2 个 json 文件,想将它们合并到一个唯一的文件中

这里是文件的内容:

文件 1

{
    "value1": 200,
    "timestamp": 1382461861,
    "value": {
        "aaa": {
            "value1": "v1",
            "value2": "v2"
        },
        "bbb": {
            "value1": "v1",
            "value2": "v2"
        },
        "ccc": {
            "value1": "v1",
            "value2": "v2"
        }
    }
}

文件2

{
    "status": 200,
    "timestamp": 1382461861,
    "value": {
        "aaa": {
            "value3": "v3",
            "value4": 4
        },
        "bbb": {
            "value3": "v3"
        },      
        "ddd": {
            "value3": "v3",
            "value4": 4
        }
    }
}

预期结果

{
    "value": {
        "aaa": {
            "value1": "v1",
            "value2": "v2",
            "value3": "v3",
            "value4": 4
        },
        "bbb": {
            "value1": "v1",
            "value2": "v2",
            "value3": "v3"
        },
        "ccc": {
            "value1": "v1",
            "value2": "v2"
        },
        "ddd": {
            "value3": "v3",
            "value4": 4
        }
    }
}

我尝试了很多组合,但我得到的唯一结果是以下,这不是预期的结果:

{
  "ccc": {
    "value2": "v2",
    "value1": "v1"
  },
  "bbb": {
    "value2": "v2",
    "value1": "v1"
  },
  "aaa": {
    "value2": "v2",
    "value1": "v1"
  }
}
{
  "ddd": {
    "value4": 4,
    "value3": "v3"
  },
  "bbb": {
    "value3": "v3"
  },
  "aaa": {
    "value4": 4,
    "value3": "v3"
  }
}

使用此命令:

jq -s '.[].value' file1 file2
4

6 回答 6

234

从 1.4 开始,操作员现在可以做到这一点*。当给定两个对象时,它将递归地合并它们。例如,

jq -s '.[0] * .[1]' file1 file2

重要提示:注意-s (--slurp)标志,它将文件放在同一个数组中。

会让你:

{
  "value1": 200,
  "timestamp": 1382461861,
  "value": {
    "aaa": {
      "value1": "v1",
      "value2": "v2",
      "value3": "v3",
      "value4": 4
    },
    "bbb": {
      "value1": "v1",
      "value2": "v2",
      "value3": "v3"
    },
    "ccc": {
      "value1": "v1",
      "value2": "v2"
    },
    "ddd": {
      "value3": "v3",
      "value4": 4
    }
  },
  "status": 200
}

如果您还想摆脱其他键(如您的预期结果),一种方法是:

jq -s '.[0] * .[1] | {value: .value}' file1 file2

或者可能更有效(因为它不合并任何其他值):

jq -s '.[0].value * .[1].value | {value: .}' file1 file2
于 2014-07-23T07:27:13.910 回答
91

使用jq -s add

$ echo '{"a":"foo","b":"bar"} {"c":"baz","a":0}' | jq -s add
{
  "a": 0,
  "b": "bar",
  "c": "baz"
}

这会将所有 JSON 文本从标准输入读取到一个数组中(jq -s这样做),然后“减少”它们。

(add定义为def add: reduce .[] as $x (null; . + $x);,它遍历输入数组/对象的值并添加它们。对象添加 == 合并。)

于 2014-06-19T06:29:44.690 回答
41

谁知道你是否还需要它,但这是解决方案。

一旦你--slurp选择了,这很容易!

--slurp/-s:
    Instead of running the filter for each JSON object in the input,
    read the entire input stream into a large array and run the filter just once.

然后+操作员会做你想做的事:

jq -s '.[0] + .[1]' config.json config-user.json

(注意:如果你想合并内部对象而不是仅仅用右文件覆盖左边的文件,你需要手动完成)

于 2014-03-01T22:21:47.307 回答
38

*这是一个在任意数量的对象上递归(使用)工作的版本:

echo '{"A": {"a": 1}}' '{"A": {"b": 2}}' '{"B": 3}' |\
  jq --slurp 'reduce .[] as $item ({}; . * $item)'

{
  "A": {
    "a": 1,
    "b": 2
  },
  "B": 3
}
于 2016-03-25T10:17:26.840 回答
8

首先,{"value": .value} 可以缩写为 {value}。

其次,--argfile 选项(在 jq 1.4 和 jq 1.5 中可用)可能很有趣,因为它避免了使用 --slurp 选项。

把这些放在一起,两个文件中的两个对象可以按照指定的方式组合起来,如下所示:

$ jq -n --argfile o1 file1 --argfile o2 file2 '$o1 * $o2 | {value}'

'-n' 标志告诉 jq 不要从标准输入读取,因为输入来自此处的 --argfile 选项。

关于 --argfile 的注意事项

jq 手册不推荐使用--argfile,因为它的语义很重要:如果指定的输入文件恰好包含一个 JSON 实体,则按原样读取该实体;否则,流中的项目将包装在一个数组中。

如果您对使用 --argfile 感到不舒服,您可能希望考虑几种替代方法。这样做时,请确保 using在命令行选项与多个文件一起使用时--slurpfile不会导致效率低下。-s

于 2015-08-10T04:43:15.640 回答
6

这可用于合并命令中指定的任意数量的文件:

jq -rs 'reduce .[] as $item ({}; . * $item)' file1.json file2.json file3.json ... file10.json

或者这个对于任意数量的文件

jq -rs 'reduce .[] as $item ({}; . * $item)' ./*.json

于 2019-10-30T08:51:06.223 回答