jq Compare two files and output the difference in text format

561 views Asked by At

I have 2 files

file_one.json

{
    "releases": [
        {
            "name": "bpm",
            "version": "1.1.5"
        },
        {
            "name": "haproxy",
            "version": "9.8.0"
        },
        {
            "name": "test",
            "version": "10"
        }
    ]
}

and file_two.json

{
    "releases": [
        {
            "name": "bpm",
            "version": "1.1.6"
        },
        {
            "name": "haproxy",
            "version": "9.8.1"
        },
        {
            "name": "test",
            "version": "10"
        }
    ]
}

In file 2 the versions were changed and i need to echo the new changes. I have used the following command to see the changes:

diff -C 2  <(jq -S . file_one.json) <(jq -S . file_two.json)

But than i need to format the output to something like this. I need to output text:

The new versions are:
bpm 1.1.6
haproxy 9.8.1
1

There are 1 answers

2
Aaron On BEST ANSWER

You may be able to use the following jq command :

jq --slurp -r 'map(.releases) | add
   | group_by(.name)
   | map(unique | select(length > 1) | max_by(.version))
   | map("\(.name) : \(.version)") | join("\n")'
   file_one.json file_two.json

It first merges the two releases arrays, groups the elements by name, then unicize the elements of the resulting arrays, remove the arrays with a single element (the versions that were identic between the two files), then map the arrays into their greatest element (by version) and finally format those for display.

You can try it here.

A few particularities that might make this solution incorrect for your use :

  • it doesn't only report version upgrades, but also version downgrades. However, it always returns the greatest version, disregarding which file contains it.
  • the version comparison is alphabetic. It's okay with your sample, but it can fail for multi-digits versions (e.g. 1.1.5 is considered greater than 1.1.20 because 5 > 2). This could be fixed but might not be problematic depending on your versionning scheme.

Edit following your updated request in the comments : the following jq command will output the versions changed between the first file and the second. It nicely handles downgrades and somewhat handles products that have appeared or disappeared in the second file (although it always shows the version as version --> null whether it is a product that appeared or disappeared).

jq --slurp -r 'map(.releases) | add
   | group_by(.name) 
   | map(select(.[0].version != .[1].version)) 
   | map ("\(.[0].name) : \(.[0].version) --> \(.[1].version)")
   | join("\n")' file_one.json file_two.json

You can try it here.