Skip to content

Instantly share code, notes, and snippets.

@datygra
Forked from ttscoff/appinfo.md
Created June 11, 2024 18:07
Show Gist options
  • Save datygra/95409a174f2c919e40f24e6564e4aa7c to your computer and use it in GitHub Desktop.
Save datygra/95409a174f2c919e40f24e6564e4aa7c to your computer and use it in GitHub Desktop.

Revisions

  1. @ttscoff ttscoff revised this gist Oct 28, 2021. 1 changed file with 4 additions and 2 deletions.
    6 changes: 4 additions & 2 deletions appinfo.rb
    Original file line number Diff line number Diff line change
    @@ -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
    @@ -134,7 +136,7 @@ def parse_info(info)
    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/
    val += " (Unviversal Binary)" if val =~ /Intel/ && val =~ /Apple Silicon/
    values[m[1]] = val
    ''
    end
  2. @ttscoff ttscoff revised this gist Oct 20, 2021. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions appinfo.md
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    # appinfo

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

    ![](http://assets.brettterpstra.com/appinfo-800.jpg)

    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.
    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.
  3. @ttscoff ttscoff revised this gist Oct 20, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion appinfo.rb
    Original file line number Diff line number Diff line change
    @@ -134,7 +134,7 @@ def parse_info(info)
    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/
    val += ' (Unviversal Binary)' if val =~ /Intel/ && val =~ /Apple Silicon/
    values[m[1]] = val
    ''
    end
  4. @ttscoff ttscoff revised this gist Oct 20, 2021. No changes.
  5. @ttscoff ttscoff revised this gist Oct 20, 2021. 1 changed file with 6 additions and 2 deletions.
    8 changes: 6 additions & 2 deletions appinfo.md
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,7 @@
    Usage: appinfo [APP NAME]
    # appinfo

    ![](http://assets.brettterpstra.com/appinfo-800.jpg)
    A script for getting details of installed Mac apps.

    ![](http://assets.brettterpstra.com/appinfo-800.jpg)

    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.
  6. @ttscoff ttscoff renamed this gist Oct 20, 2021. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  7. @ttscoff ttscoff revised this gist Oct 20, 2021. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,3 @@
    Usage: appinfo [APP NAME]

    ![](http://assets.brettterpstra.com/appinfo-800.jpg)
  8. @ttscoff ttscoff revised this gist Oct 20, 2021. 1 changed file with 55 additions and 23 deletions.
    78 changes: 55 additions & 23 deletions appinfo.rb
    Original file line number Diff line number Diff line change
    @@ -16,7 +16,7 @@
    Keys for dates are converted to localized short date format
    === Config
    :use_imgcat: If you have iTerm2 and imgcat installed, print out an icon
    :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:
    @@ -33,17 +33,19 @@
    =end

    CONFIG = {
    :use_imgcat => true,
    :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'
    'kMDItemCopyright' => 'Copyright',
    'kMDItemExecutableArchitectures' => 'Architecture'
    }
    }

    @@ -83,21 +85,41 @@ def to_human(fmt=false)

    def find_app(app)
    location = nil
    res = %x{mdfind 'kind:app filename:"#{app}"' | grep -E '\.app$' | head -n 1}
    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
    if res && res.length > 0
    location = res.strip

    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
    return location.nil? ? false : location
    end

    def show_icon(app_path)
    if CONFIG[:use_imgcat] && File.exists?('/usr/local/bin/imgcat')
    app_icon = %x{defaults read "#{app_path}/Contents/Info" CFBundleIconFile}.strip.sub(/(\.icns)?$/,'.icns')
    res = %x{mkdir -p ${TMPDIR}appinfo && sips -s format png --resampleHeightWidthMax 125 "#{app_path}/Contents/Resources/#{app_icon}" --out "${TMPDIR}appinfo/#{app_icon}.png"} # > /dev/null 2>&1
    $stdout.puts %x{imgcat "${TMPDIR}appinfo/#{app_icon}.png" && rm "${TMPDIR}appinfo/#{app_icon}.png"}
    if CONFIG[:show_icon] && (exec_available('imgcat') || exec_available('chafa'))
    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'
    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

    @@ -106,16 +128,27 @@ def parse_info(info)
    if class_exists? 'Encoding'
    info = info.force_encoding('utf-8')
    end
    info.split("\n").each {|line|

    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(/"/,'')
    }
    values[sp[0]] = sp[1].gsub(/"/, '')
    end
    values
    end


    def get_info(appname)
    app = appname.sub(/\.app$/,'')
    app = appname # .sub(/\.app$/,'')
    found = find_app(app)
    if found
    keys = "-name " + CONFIG[:keys].keys.join(' -name ')
    @@ -132,10 +165,11 @@ def get_info(appname)
    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
    val = appinfo[k]&.strip || 'None'
    val = case k
    when /Size$/
    val.to_human
    @@ -150,18 +184,16 @@ def info(app)
    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]
    }
    show_icon(appinfo['location'])
    end
    end

    def exit_help(code=0)
    output =<<ENDOUT
    Shows keys from Spotlight data for an app
    Usage:
    #{File.basename(__FILE__)} [app name]
    output = <<~ENDOUT
    Shows keys from Spotlight data for an app
    Usage:
    ENDOUT
    #{File.basename(__FILE__)} [app name]
    ENDOUT
    puts output
    Process.exit code.to_i
    end
  9. @ttscoff ttscoff revised this gist Aug 28, 2017. 1 changed file with 2 additions and 4 deletions.
    6 changes: 2 additions & 4 deletions appinfo.rb
    Original file line number Diff line number Diff line change
    @@ -84,9 +84,8 @@ def to_human(fmt=false)
    def find_app(app)
    location = nil
    res = %x{mdfind 'kind:app filename:"#{app}"' | grep -E '\.app$' | head -n 1}
    begin
    if class_exists? 'Encoding'
    res = res.force_encoding('utf-8')
    rescue
    end
    if res && res.length > 0
    location = res.strip
    @@ -104,9 +103,8 @@ def show_icon(app_path)

    def parse_info(info)
    values = {}
    begin
    if class_exists? 'Encoding'
    info = info.force_encoding('utf-8')
    rescue
    end
    info.split("\n").each {|line|
    sp = line.split(/\s*=\s*/)
  10. @ttscoff ttscoff revised this gist Aug 28, 2017. 1 changed file with 51 additions and 23 deletions.
    74 changes: 51 additions & 23 deletions appinfo.rb
    Original file line number Diff line number Diff line change
    @@ -1,28 +1,36 @@
    #!/usr/bin/env ruby
    require 'date'
    ###
    # 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
    # :use_imgcat if you have iTerm2 and imgcat installed, print out an icon
    # :keys The keys to parse and their "pretty" form for printing
    # Output in the order listed
    # defaults:
    # 'location' => 'Location',
    # 'kMDItemCFBundleIdentifier' => 'Bundle ID',
    # 'kMDItemPhysicalSize' => 'Size',
    # 'kMDItemVersion' => 'Version',
    # 'kMDItemContentCreationDate' => 'Released',
    # 'kMDItemAppStorePurchaseDate' => 'Purchased',
    # 'kMDItemLastUsedDate' => 'Last Used',
    # 'kMDItemAppStoreCategory' => 'Category',
    # 'kMDItemCopyright' => 'Copyright'
    ###################################################
    # 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
    :use_imgcat: If you have iTerm2 and imgcat 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 = {
    :use_imgcat => true,
    @@ -39,6 +47,18 @@
    }
    }

    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
    @@ -64,6 +84,10 @@ def to_human(fmt=false)
    def find_app(app)
    location = nil
    res = %x{mdfind 'kind:app filename:"#{app}"' | grep -E '\.app$' | head -n 1}
    begin
    res = res.force_encoding('utf-8')
    rescue
    end
    if res && res.length > 0
    location = res.strip
    end
    @@ -80,6 +104,10 @@ def show_icon(app_path)

    def parse_info(info)
    values = {}
    begin
    info = info.force_encoding('utf-8')
    rescue
    end
    info.split("\n").each {|line|
    sp = line.split(/\s*=\s*/)
    values[sp[0]] = sp[1].gsub(/"/,'')
  11. @ttscoff ttscoff revised this gist Jul 24, 2017. No changes.
  12. @ttscoff ttscoff created this gist Jul 24, 2017.
    149 changes: 149 additions & 0 deletions appinfo.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,149 @@
    #!/usr/bin/env ruby
    require 'date'
    ###
    # 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
    # :use_imgcat if you have iTerm2 and imgcat installed, print out an icon
    # :keys The keys to parse and their "pretty" form for printing
    # Output in the order listed
    # defaults:
    # 'location' => 'Location',
    # 'kMDItemCFBundleIdentifier' => 'Bundle ID',
    # 'kMDItemPhysicalSize' => 'Size',
    # 'kMDItemVersion' => 'Version',
    # 'kMDItemContentCreationDate' => 'Released',
    # 'kMDItemAppStorePurchaseDate' => 'Purchased',
    # 'kMDItemLastUsedDate' => 'Last Used',
    # 'kMDItemAppStoreCategory' => 'Category',
    # 'kMDItemCopyright' => 'Copyright'

    CONFIG = {
    :use_imgcat => true,
    :keys => {
    'location' => 'Location',
    'kMDItemCFBundleIdentifier' => 'Bundle ID',
    'kMDItemPhysicalSize' => 'Size',
    'kMDItemVersion' => 'Version',
    'kMDItemContentCreationDate' => 'Released',
    'kMDItemAppStorePurchaseDate' => 'Purchased',
    'kMDItemLastUsedDate' => 'Last Used',
    'kMDItemAppStoreCategory' => 'Category',
    'kMDItemCopyright' => 'Copyright'
    }
    }

    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
    res = %x{mdfind 'kind:app filename:"#{app}"' | grep -E '\.app$' | head -n 1}
    if res && res.length > 0
    location = res.strip
    end
    return location.nil? ? false : location
    end

    def show_icon(app_path)
    if CONFIG[:use_imgcat] && File.exists?('/usr/local/bin/imgcat')
    app_icon = %x{defaults read "#{app_path}/Contents/Info" CFBundleIconFile}.strip.sub(/(\.icns)?$/,'.icns')
    res = %x{mkdir -p ${TMPDIR}appinfo && sips -s format png --resampleHeightWidthMax 125 "#{app_path}/Contents/Resources/#{app_icon}" --out "${TMPDIR}appinfo/#{app_icon}.png"} # > /dev/null 2>&1
    $stdout.puts %x{imgcat "${TMPDIR}appinfo/#{app_icon}.png" && rm "${TMPDIR}appinfo/#{app_icon}.png"}
    end
    end

    def parse_info(info)
    values = {}
    info.split("\n").each {|line|
    sp = line.split(/\s*=\s*/)
    values[sp[0]] = sp[1].gsub(/"/,'')
    }
    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
    longest_key = CONFIG[:keys].values.longest_element
    CONFIG[:keys].each {|k,v|
    key = v
    val = appinfo[k].strip
    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]
    }
    show_icon(appinfo['location'])
    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