'jq - return array value if its length is not null

I have a report.json generated by a gitlab pipeline. It looks like:

{"version":"14.0.4","vulnerabilities":[{"id":"64e69d1185ecc48a1943141dcb6dbd628548e725f7cef70d57403c412321aaa0","category":"secret_detection"....and so on

If no vulnerabilities found, then "vulnerabilities":[]. I'm trying to come up with a bash script that would check if vulnerabilities length is null or not. If not, print the value of the vulnerabilities key. Sadly, I'm very far from scripting genius, so it's been a struggle. While searching web for a solution to this, I've come across jq. It seems like select() should do the job. I've tried:

jq "select(.vulnerabilities!= null)" report.json

but it returned {"version":"14.0.4","vulnerabilities":[{"id":"64e69d1185ecc48a194314... instead of expected "vulnerabilities":[{"id":"64e69d1185ecc48a194314... and

map(select(.vulnerabilities != null)) report.json

returns "No matches found"

Would you mind pointing out what's wrong apart from my 0 experience with bash and JSON parsing? :)

Thanks in advance



Solution 1:[1]

Just use . filter to identify the object vulnerabilities.

these is some cases below

$ jq '.vulnerabilities' <<END
heredoc> {"version":"14.0.4","vulnerabilities":[{"id":"64e69d1185ecc48a1943141dcb6dbd628548e725f7cef70d57403c412321aaa0","category":"secret_detection"}]}
heredoc> END
[
  {
    "id": "64e69d1185ecc48a1943141dcb6dbd628548e725f7cef70d57403c412321aaa0",
    "category": "secret_detection"
  }
]

if vulnerabilities null, then jq will return null

$ jq '.vulnerabilities' <<END
{"version":"14.0.4","vulnerabilities":null}
END
null

then with pipe |, you can change it to any output you wanted.

  • change null to []: .vulnerabilities | if . == null then [] else . end

  • filter empty array: .vulnerabilities | select(length > 0)

For further information about jq filters, you can read the jq manual.

Solution 2:[2]

  • Assuming, by "print the value of the vulnerabilities key" you mean the value of an item's id field. You can retrieve it using .id and have it extracted to bash with the -r option.
  • If in case the array is not empty you want all of the "keys", iterate over the array using .[]. If you just wanted a specific key, let's say the first, address it using a 0-based index: .[0].
  • To check the length of an array there is a dedicated length builtin. However, as your final goal is to extract, you can also attempt to do so right anyway, suppress a potential unreachability error using the ? operator, and have your bash script read an appropriate exit status using the -e option.

Your bash script then could include the following snippet

if key=$(jq -re '.vulnerabilities[0].id?' report.json)
then
  # If the array was not empty, $key contains the first key
  echo "There is a vulnerability in key $key."
fi

# or

if keys=$(jq -re '.vulnerabilities[].id?' report.json)
then
  # If the array was not empty, $keys contains all the keys
  for k in $keys
  do echo "There is a vulnerability in key $k."
  done
fi

Solution 3:[3]

Firstly, please note that in the JSON world, it is important to distinguish between [] (the empty array), the values 0 and null, and the absence of a value (e.g. as the result of the absence of a key in an object).

In the following, I'll assume that the output should be the value of .vulnerabilities if it is not `[]', or nothing otherwise:

< sample.json jq '
  select(.vulnerabilities != []).vulnerabilities
'

If the goal were to differentiate between two cases based on the return code from jq, you could use the -e command-line option.

Solution 4:[4]

You can use if-then-else.

Filter

if (.vulnerabilities | length) > 0 then {vulnerabilities} else empty end

Input

{
  "version": "1.1.1",
  "vulnerabilities": [
    {
      "id": "111",
      "category": "secret_detection"
    },
    {
      "id": "112",
      "category": "secret_detection"
    }
  ]
}
{
  "version": "1.2.1",
  "vulnerabilities": [
    {
      "id": "121",
      "category": "secret_detection 2"
    }
  ]
}
{
  "version": "3.1.1",
  "vulnerabilities": []
}
{
  "version": "4.1.1",
  "vulnerabilities": [
    {
      "id": "411",
      "category": "secret_detection 4"
    },
    {
      "id": "412",
      "category": "secret_detection"
    },
    {
      "id": "413",
      "category": "secret_detection"
    }
  ]
}

Output

{
  "vulnerabilities": [
    {
      "id": "111",
      "category": "secret_detection"
    },
    {
      "id": "112",
      "category": "secret_detection"
    }
  ]
}
{
  "vulnerabilities": [
    {
      "id": "121",
      "category": "secret_detection 2"
    }
  ]
}
{
  "vulnerabilities": [
    {
      "id": "411",
      "category": "secret_detection 4"
    },
    {
      "id": "412",
      "category": "secret_detection"
    },
    {
      "id": "413",
      "category": "secret_detection"
    }
  ]
}

Demo

https://jqplay.org/s/wicmr4uVRm

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 pmf
Solution 3
Solution 4 Logan Lee