This is possible using Homebrew’s JSON API as well as some jq
magic (brew install jq
).
Assuming none of your .app
filenames contain a newline (very unlikely), you can get the list as a JSON array with a command combining ls
and jq
. However since we’ll use that list as a lookup it’s better to create an object instead:
ls /Applications \
| \grep '\.app$' \
| jq -Rsc 'split("\n")[:-1]|map({(.):1})|add'
This creates an object with each app as a key and 1
as a value (the value has no importance here). It outputs something like:
{"1Password 7.app":1,"Amphetamine.app":1, "Firefox.app":1, …}
You can list all 3,500+ installable casks using brew search --casks
. In order to get a JSON describing one or more cask(s), including the .app
they install, you can use brew cask info --json=v1 <cask> …
.
Combining these two, we can get a huge JSON describing all installable casks with:
brew search --casks '' \
| xargs brew info --cask --json=v2 \
> allcasks.json
This command takes ~10s on my machine so saving it in a file is a good idea.
We can now filter this list to extract only the casks that install .app
s from our earlier list:
cat allcasks.json \
| jq -r --argjson list '{…the list…}' '.[]|.[]|(.artifacts|map(.[]?|select(type=="string")|select(in($list)))|first) as $app|select($app)|"\(.token): \($app)"'
Replace {…the list…}
with the object we created earlier.
This prints something like:
1password: 1Password 7.app
firefox: Firefox.app
google-chrome: Google Chrome.app
…
If you feel adventurous, here is a one-liner that does all these commands at once:
brew search --casks '' \
|xargs brew info --cask --json=v2 \
|jq -r --argjson l "$(ls /Applications|\grep '\.app$'|jq -Rsc 'split("\n")[:-1]|map({(.):1})|add')" '.[]|.[]|(.artifacts|map(.[]?|select(type=="string")|select(in($l)))|first) as $a|select($a)|"\(.token): \($a)"'
Breakdown of the jq
command:
.[] # flatten the list
| # then for each element:
.[] # flatten the list
| # then for each element:
( # take its artifacts
.artifacts
# then for each one of them
| map(
# take only arrays
.[]?
# select their string elements
| select(type=="string")
# that are also in the list
| select(in($list)
)
)
# take the first matching artifact
| first)
# and store it in $app
as $app
# then take only the elements with a non-empty $app
| select($app)
# and print their name (.token) and the app ($app)
|"\(.token): \($app)"