Skip to content

Instantly share code, notes, and snippets.

@ttscoff
Last active Nov 21, 2021
Embed
What would you like to do?
appinfo: A quick ruby script for Mac that returns some information for a specified Mac app in Terminal. Includes icon display if using imgcat or chafa are available.

appinfo

A script for getting details of installed Mac apps. Like Get Info but faster and cooler.

Save the script below as appinfo somewhere in your path. Make it executable with chmod a+x appinfo. Then just run appinfo APPNAME to get info on any installed app.

#!/usr/bin/env ruby
require 'date'
###################################################
# iii fff #
# aa aa pp pp pp pp nn nnn ff oooo #
# aa aaa ppp pp ppp pp iii nnn nn ffff oo oo #
# aa aaa pppppp pppppp iii nn nn ff oo oo #
# aaa aa pp pp iii nn nn ff oooo #
# pp pp #
###################################################
=begin appinfo
Shows keys from spotlight data for an app
usage: 'appinfo [app name]'
Keys for sizes are converted to human-readable numbers (e.g. 25.3MB)
Keys for dates are converted to localized short date format
=== Config
:show_icon: If you have imgcat or chafa installed, print out an icon
:keys: The keys to parse and their "pretty" form for printing Output in
the order listed
==== Default keys:
'location' => 'Location',
'kMDItemCFBundleIdentifier' => 'Bundle ID',
'kMDItemPhysicalSize' => 'Size',
'kMDItemVersion' => 'Version',
'kMDItemContentCreationDate' => 'Released',
'kMDItemAppStorePurchaseDate' => 'Purchased',
'kMDItemLastUsedDate' => 'Last Used',
'kMDItemAppStoreCategory' => 'Category',
'kMDItemCopyright' => 'Copyright'
=end
CONFIG = {
:show_icon => true,
:keys => {
'location' => 'Location',
'kMDItemCFBundleIdentifier' => 'Bundle ID',
'kMDItemAlternateNames' => 'Alternate Names',
'kMDItemPhysicalSize' => 'Size',
'kMDItemVersion' => 'Version',
'kMDItemContentCreationDate' => 'Released',
'kMDItemAppStorePurchaseDate' => 'Purchased',
'kMDItemLastUsedDate' => 'Last Used',
'kMDItemAppStoreCategory' => 'Category',
'kMDItemCopyright' => 'Copyright',
'kMDItemExecutableArchitectures' => 'Architecture'
}
}
def class_exists?(class_name)
klass = Module.const_get(class_name)
return klass.is_a?(Class)
rescue NameError
return false
end
if class_exists? 'Encoding'
Encoding.default_external = Encoding::UTF_8 if Encoding.respond_to?('default_external')
Encoding.default_internal = Encoding::UTF_8 if Encoding.respond_to?('default_internal')
end
class Array
def longest_element
group_by(&:size).max.last[0].length
end
end
class String
def to_human(fmt=false)
n = self.to_i
count = 0
formats = %w(B KB MB GB TB PB EB ZB YB)
while (fmt || n >= 1024) && count < 8
n /= 1024.0
count += 1
break if fmt && formats[count][0].upcase =~ /#{fmt[0].upcase}/
end
format("%.2f",n) + formats[count]
end
end
def find_app(app)
location = nil
narrow = ' -onlyin /System/Applications -onlyin /Applications -onlyin /Applications/Setapp -onlyin /Applications/Utilities -onlyin /Developer/Applications'
res = `mdfind#{narrow} 'kind:app filename:"#{app}"' | grep -E '\.app$' | head -n 1`.strip
unless res && res.length > 0
res = `mdfind 'kind:app filename:"#{app}"' | grep -E '\.app$' | head -n 1`.strip
end
if class_exists? 'Encoding'
res = res.force_encoding('utf-8')
end
return res && !res.empty? ? res.strip : false
end
def exec_available(cli)
if File.exist?(File.expand_path(cli))
File.executable?(File.expand_path(cli))
else
system "which #{cli}", out: File::NULL, err: File::NULL
end
end
def show_icon(app_path)
if CONFIG[:show_icon] && (exec_available('imgcat') || exec_available('chafa') || exec_available("kitty"))
app_icon = `defaults read "#{app_path}/Contents/Info" CFBundleIconFile`.strip.sub(/(\.icns)?$/, '.icns')
if exec_available('imgcat')
cmd = 'imgcat'
elsif exec_available('chafa')
cmd = 'chafa -s 15x15 -f iterm'
elsif exec_available('kitty')
cmd = 'kitty +kitten icat --align=left'
end
res = `mkdir -p ${TMPDIR}appinfo && sips -s format png --resampleHeightWidthMax 256 "#{app_path}/Contents/Resources/#{app_icon}" --out "${TMPDIR}appinfo/#{app_icon}.png"` # > /dev/null 2>&1
$stdout.puts `#{cmd} "${TMPDIR}appinfo/#{app_icon}.png" && rm "${TMPDIR}appinfo/#{app_icon}.png"`
end
end
def parse_info(info)
values = {}
if class_exists? 'Encoding'
info = info.force_encoding('utf-8')
end
info.gsub!(/(\S+)\s*=\s*\((.*?)\)/m) do
m = Regexp.last_match
val = m[2].strip.split(/\n/).delete_if { |i| i.strip.empty? }.map { |l|
l.strip.gsub(/"/, '').sub(/,$/, '').sub(/x86_64/, 'Intel').sub(/arm64/, 'Apple Silicon')
}.join(', ')
val += " (Unviversal Binary)" if val =~ /Intel/ && val =~ /Apple Silicon/
values[m[1]] = val
''
end
info.split(/\n/).delete_if(&:empty?).each do |line|
sp = line.split(/\s*=\s*/)
values[sp[0]] = sp[1].gsub(/"/, '')
end
values
end
def get_info(appname)
app = appname # .sub(/\.app$/,'')
found = find_app(app)
if found
keys = "-name " + CONFIG[:keys].keys.join(' -name ')
res = %x{mdls #{keys} "#{found}"}
result = parse_info(res)
result['location'] = found
return result
else
$stdout.puts %Q{App "#{app}" not found.}
Process.exit 1
end
end
def info(app)
appinfo = get_info(app)
if appinfo && appinfo.length > 0
show_icon(appinfo['location'])
longest_key = CONFIG[:keys].values.longest_element
CONFIG[:keys].each {|k,v|
key = v
val = appinfo[k]&.strip || 'None'
val = case k
when /Size$/
val.to_human
when /Date$/
if appinfo[k].strip =~ /^\d{4}-\d{2}-\d{2}/
Date.parse(val.strip).strftime('%D') rescue val
end
else
val
end
val = val =~ /\(null\)/ ? "\033[0;36;40mUnknown\033[0m" : "\033[1;37;40m#{val}\033[0m"
$stdout.puts "\033[0;32;40m%#{longest_key}s: %s" % [key, val]
}
end
end
def exit_help(code=0)
output = <<~ENDOUT
Shows keys from Spotlight data for an app
Usage:
#{File.basename(__FILE__)} [app name]
ENDOUT
puts output
Process.exit code.to_i
end
if ARGV.length == 0
exit_help(1)
elsif ARGV[0] =~ /^-?h(elp)?$/
exit_help
else
info(ARGV.join(" "))
end
@achabotl

This comment has been minimized.

Copy link

@achabotl achabotl commented Oct 22, 2021

Use this diff to show the app icon in the Kitty terminal emulator.

diff --git i/appinfo w/appinfo
index e2c8301..f51bdef 100755
--- i/appinfo
+++ w/appinfo
@@ -107,13 +107,15 @@ def exec_available(cli)
 end
 
 def show_icon(app_path)
-  if CONFIG[:show_icon] && (exec_available('imgcat') || exec_available('chafa'))
+  if CONFIG[:show_icon] && (exec_available('imgcat') || exec_available('chafa') || exec_available("kitty"))
     app_icon = `defaults read "#{app_path}/Contents/Info" CFBundleIconFile`.strip.sub(/(\.icns)?$/, '.icns')
 
     if exec_available('imgcat')
       cmd = 'imgcat'
     elsif exec_available('chafa')
       cmd = 'chafa -s 15x15 -f iterm'
+    elsif exec_available('kitty')
+      cmd = 'kitty +kitten icat --align=left'
     end
 
     res = `mkdir -p ${TMPDIR}appinfo && sips -s format png --resampleHeightWidthMax 256 "#{app_path}/Contents/Resources/#{app_icon}" --out "${TMPDIR}appinfo/#{app_icon}.png"` #  > /dev/null 2>&1
@cocoonkid

This comment has been minimized.

Copy link

@cocoonkid cocoonkid commented Oct 25, 2021

I get "inappropriate ioctl for device" instead of a logo for any app. (Apple M1).
But I get the infos so that's great! Thanks for this.

@bocciaman

This comment has been minimized.

Copy link

@bocciaman bocciaman commented Oct 27, 2021

I need help setting this up. I'm a complete beginner.

I've saved the code above in a file within my home folder called appinfo.sh. Now, to execute it do I chmod a+x /Users/username/appinfo.sh in terminal?

Then

I just appinfo <app name>

@ryanfitzer

This comment has been minimized.

Copy link

@ryanfitzer ryanfitzer commented Oct 27, 2021

@bocciaman

  1. Save file to /usr/local/bin/appinfo (no .sh needed)
  2. Run chmod a+x /usr/local/bin/appinfo
  3. Run which appinfo. Result should print /usr/local/bin/appinfo
  4. Now run something like appinfo Preview.
@ryanfitzer

This comment has been minimized.

Copy link

@ryanfitzer ryanfitzer commented Oct 27, 2021

Running appinfo Preview gives me the info, but no app icon (2015 MBP macOS 10.15.7). But the info is good enough.

Thanks @ttscoff!

@bocciaman

This comment has been minimized.

Copy link

@bocciaman bocciaman commented Oct 27, 2021

@ryanfitzer Thank you for the help, your instructions are perfect. It worked! Just FYI, I tried three apps and never saw an icon either but my machine may be old and decrepit to run this app perfectly (Intel-based '09 iMac).

Thanks, @ttscoff for making this tool!

@ryanfitzer

This comment has been minimized.

Copy link

@ryanfitzer ryanfitzer commented Oct 28, 2021

Reread the instructions :) Need to have either imgcat or chafa installed to get images. I'm using iTerm2 and made sure "Shell Integration" was in installed. Ran which imgcat and got imgcat: aliased to /Users/ryanfitzer/.iterm2/imgcat. Restarted iTerm2, but still no images.

So I used Homebrew to install chafa by running brew install chafa. Took about 10 minutes to complete. Images are now working.

@cocoonkid

This comment has been minimized.

Copy link

@cocoonkid cocoonkid commented Oct 28, 2021

inappropriate ioctl for device
       Location: /Applications/Alfred 4.app
      Bundle ID: com.runningwithcrayons.Alfred
Alternate Names: Alfred 4.app
           Size: 14.55MB
        Version: 4.6
       Released: 10/15/21
      Purchased:
      Last Used: 10/26/21
       Category: Productivity
      Copyright: Copyright © 2019 Running with Crayons. All rights reserved.
   Architecture: Apple Silicon, Intel (Unviversal Binary)
Time: 0h:00m:03s

gen-sync/projects_assets/setup_macos via 💎 v2.6.3 on ☁️   took 3s
❯ which imgcat
imgcat: aliased to /Users/cocoonkid/.iterm2/imgcat

gen-sync/projects_assets/setup_macos via 💎 v2.6.3 on ☁️
❯ which chafa
/opt/homebrew/bin/chafa

Thank you for clearing it up @ryanfitzer
I couldn't make it work with imagers though. Neither in iterm2 nor alacritty.

But not really that important anyway :-)

@ttscoff

This comment has been minimized.

Copy link
Owner Author

@ttscoff ttscoff commented Oct 28, 2021

Use this diff to show the app icon in the Kitty terminal emulator.

Added to the script, thanks!

@ttscoff

This comment has been minimized.

Copy link
Owner Author

@ttscoff ttscoff commented Oct 28, 2021

I'm not sure why so many are having issues with imgcat, I can't replicate. which imgcat gives me ~/.iterm2/imgcat, where iTerm installed it. AFAIK I didn't do anything else to it. Definitely shows me an image without issue.

Curious about @cocoonkid 's error, inappropriate ioctl for device. Would be good to know exactly which shell command produced that error. The only part of the show_icon function that outputs to terminal is a $stdout.puts call that shows the STDOUT output of either imgcat or chafa, depending on which it finds available first (in that order). Would need to know which one it was attempting to run to solve that. I don't think the sips call would produce that error.

The other thing that might affect people with chafa is that I have it forced to --format iterm. The other options are kitty, sixels, and symbols, so feel free to play with that in line 116 if you need to.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment