Home > OS >  jq: how to loop through sub arrays
jq: how to loop through sub arrays

Time:12-15

I'm having the following dataset:

{
  "data": {
    "activeFindings": {
      "findings": [
        {
          "findingId": "someFindingID#84209",
          "products": [
            "hostA.corp.somedomain.org",
            "hostB.corp.somedomain.org"
          ],
          "totalAffectedObjectsCount": 6
        },
        {
          "findingId": "someFindingID#2145016",
          "products": [
            "hostC.corp.somedomain.org"
          ],
          "totalAffectedObjectsCount": 1
        },
        {
          "findingId": "someFindingID#67129",
          "products": [
            "hostD.corp.somedomain.org"
          ],
          "totalAffectedObjectsCount": 4
        },
        {
          "findingId": "someFindingID#67774",
          "products": [
            "hostA.corp.somedomain.org"
          ],
          "totalAffectedObjectsCount": 6
        }
      ]
    }
  }
}

The following command (though the first result returns null) will give the list of findingID and its associated host(s):

cat test | jq -r '.data[] | .. | "\(.findingId?) \(.products?)"'
null null
someFindingID#84209 ["hostA.corp.somedomain.org","hostB.corp.somedomain.org"]
someFindingID#2145016 ["hostC.corp.somedomain.org","hostE.corp.somedomain.org","hostG.corp.somedomain.org"]
someFindingID#67129 ["hostD.corp.somedomain.org"]
someFindingID#67774 ["hostA.corp.somedomain.org"]

What I'd like to achieve is to loop through each values and pass the findingId & products as arguments in a bash script.

The following:

someFindingID#84209 ["hostA.corp.somedomain.org","hostB.corp.somedomain.org"]
someFindingID#2145016 ["hostC.corp.somedomain.org","hostE.corp.somedomain.org","hostG.corp.somedomain.org"]
someFindingID#67129 ["hostD.corp.somedomain.org"]
someFindingID#67774 ["hostA.corp.somedomain.org"]

Would result in:

./somescript.sh someFindingID#84209 hostA.corp.somedomain.org
./somescript.sh someFindingID#84209 hostB.corp.somedomain.org
./somescript.sh someFindingID#2145016 hostC.corp.somedomain.org
./somescript.sh someFindingID#2145016 hostE.corp.somedomain.org
./somescript.sh someFindingID#2145016 hostG.corp.somedomain.org
./somescript.sh someFindingID#67129 hostD.corp.somedomain.org
[...]

Any help/guidance on how to achieve the above would be greatly appreciated!

Thanks,

CodePudding user response:

I'd go with something like this:

jq -r '.data[]
| .. 
| objects
| select(has("findingId"))
| "./somescript.sh \"\(.findingId)\" "   .products[]
'

You might also want to quote the "product" values as well. Or consider using @sh.

CodePudding user response:

Solution:

jq -r '
   .data[][][] |
   .products[] as $product |
   @sh "./somescript.sh \( .findingId ) \( $product )"
'

First of all, .data[] | .. returns way too many nodes.

  • .data[][][] would work great here.
  • .data.activeFindings.findings[] can used if you want to be more precise.

You ask how to loop, but you're already doing it: [] is used to loop over an array.

The catch is that you want to loop without changing the context (.). To do that, we can use as:

.products[] as $product

Finally, we want to avoid code injection bugs, so we'll use @sh "...". In the string literal that follows @sh, all interpolated values are converted into proper shell string literals.

$ jq -rn '"foo bar" | @sh "cmd \( . )"'
cmd 'foo bar'

All together, we get the following program:

.data[][][] |
.products[] as $product |
@sh "./somescript.sh \( .findingId ) \( $product )"

Demo on jqplay

  • Related