Using jq in a bash script to get an element with a certain value pattern

I’m calling the homebrew api in a shell script with the goal of getting the App name for the item before trying to install it. Luckily the api provides this in an element called artifacts. Unluckily, artifacts does not have further keys to just select one element. It can be a mixture of objects and arrays in arbitrary order.

Example data snippet for Google Chrome:

{
   "name":[
      "Google Chrome"
   ],
   "artifacts":[
      [
         "Google Chrome.app"
      ]
   ]
}

But docker is a different story:

{
   "name":[
      "Docker"
   ],
   "artifacts":[
       {
         "trash":[
           "$(brew --prefix)/bin/docker-compose.backup",
           "$(brew --prefix)/bin/docker.backup"
         ]
      },
      [
         "docker.app"
      ]
   ]
}

So I can’t just get element 0 from artifacts in my shell script like so.

artifacts=$(curl -s "https://formulae.brew.sh/api/cask/google-chrome.json" | jq -r '.artifacts[0][0]')

Is there a way to use jq to search values in the artifacts element for the pattern that ENDS with *.app? Best psuedo code I can come up with is this, but I’m getting into a bit of a mess trying to reference $element

# $cask is determined by a list the script loops through ('google-chrome' is one example)
for element in $(curl -s "https://formulae.brew.sh/api/cask/$cask.json" | jq -r '.artifacts'); do
    # if $element is an array and it's value ends with "*.app"
        # assign to a variable for later use
done

Here is a jqplay snippet of one of the more complex JSON returns. You will see a single array part way down with the sole value "docker.app". That’s what I want to target.

Asked By: Aaryn

||

This jq query works for me with the JSON for Docker:

.artifacts[] | arrays[0] | select(endswith(".app"))

The arrays filter selects arrays from elements of artifacts, and then we can look for the first elements of these arrays for one which ends with .app.

Answered By: muru

A similar query as what muru has, but returns all artefacts in any artifacts array if any of the entries in the array ends with the string .app:

.artifacts[] | select(type == "array" and any(endswith(".app")))[]

or,

.artifacts[] | arrays | select(any(endswith(".app")))[]

So if an application has some artefact thething.app and someotherthing.quux in the same array, then both are returned.

Answered By: Kusalananda
Categories: Answers Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.