4

In the finder you can control click on a document to get the contextual menu, this has 'open with..' with a submenu of all apps that can open that document. Is there a way to get this list via Terminal? Many thanks.

bmike
  • 235,889

3 Answers3

5

You can write it in Swift. Create a file called List.swift, use that code inside:

import CoreServices
import Foundation

let args = CommandLine.arguments guard args.count > 1 else { print("Missing argument") exit(1) }

let fileType = args[1]

guard let bundleIds = LSCopyAllRoleHandlersForContentType(fileType as CFString, LSRolesMask.all) else { print("Failed to fetch bundle Ids for specified filetype") exit(1) }

(bundleIds.takeRetainedValue() as NSArray) .compactMap { bundleId -> NSArray? in guard let retVal = LSCopyApplicationURLsForBundleIdentifier(bundleId as! CFString, nil) else { return nil } return retVal.takeRetainedValue() as NSArray } .flatMap { $0 } .forEach { print($0) }

and run it with:

swift List.swift "public.text"

This will print paths to all applications that can open this file type. Example output:

file:///System/Applications/TextEdit.app/
file:///Applications/Xcode-beta.app/
file:///Applications/Xcode.app/
file:///Applications/MacDown.app/
file:///Applications/Visual%20Studio.app/
file:///Applications/Google%20Chrome.app/
file:///System/Applications/Notes.app/
4

Open will consult the same database as finder to match a file type to potential apps. The name for this database is the Launch Serices Database and it's quite large and unwieldy if you dump it all.

 lsregister -dump | wc -l

I have 533,000 lines of text in my database, so you might need to narrow down what you're seeking rather than just consuming the entire fire hose of data about every possible file type and every possible application. If you don't have lsregister in your path, find it here and optionally make a sym link to it in /usr/local/bin

mdfind -name lsregister
ln -s $(mdfind -name lsregister) /usr/local/bin

But, if you want the whole enchilada - you can dump the database and dig into the glorious technical details.

Let's go two more steps down the rabbit path. Say you have a movie file on your desktop. You would use the metadata listing tool to dump all the attributes of that file (61 entries for one movie I have) and then you have to parse out the content type to match that up with the database dump from launch services.

mdls ~/Desktop/video.mov | wc -l

And to pick out just the one most specific content type (since a file has a tree of potential content types - you may have to consider them as well in your search - but since we're keeping this simple - let's assume the final type is what's matched for your case)

mdls ~/Desktop/video.mov | grep -w kMDItemContentType

So for me, that movie is categorized by spotlight indexing as a com.apple.quicktime-movie type file and in the launch services dump - one of the apps that claims to open that file is /Applications/QuickTime Player.app

Scripting this is going to be quite an exercise, but the data is there for you to examine and play with. Enjoy!

This other question has some awesome more detail and a tool called http://duti.org that might be the tool you seek. Even better, it's open source so you can see how it works.

bmike
  • 235,889
1

My solution here is similar to an answer I provided to a problem sharing some similarities with this issue.

Using JavaScript for Automation (JXA), you can retrieve a list of applications capable of opening a specific content type, denoted by a uniform type identifier.

ObjC.import('CoreServices');

const contentType = 'public.plain-text';

ObjC.deepUnwrap(
        $.LSCopyAllRoleHandlersForContentType(
                      contentType,
                      $.kLSRolesAll)
            );

To use this from the Terminal, you can execute it with osascript:

osascript -l JavaScript <<OSA
 .
 .
 .
OSA

For convenience, you may wish to create a bash function and parameterise the value of contentType:

whatOpens() {
    osascript -l JavaScript <<OSA
        ObjC.import('CoreServices');

        ObjC.deepUnwrap(
                $.LSCopyAllRoleHandlersForContentType(
                        "$1",
                        $.kLSRolesAll
                )
        ).map(x=>Application(x).name())
         .join('\n');
OSA
}

Then:

whatOpens public.plain-text

which outputs on my system:

BBEdit
MindNode
Atom
TextEdit
Pages
CotEditor
Numbers
TextMate
CJK
  • 5,512
  • This is fabulous - I need to learn more about JXA or learn to get at CoreServices from swift so it's time to dive into the repl osascript -il JavaScript and learn more. – bmike Jun 07 '20 at 15:14
  • I added this code to a bash script file, but it doesn't seem to work. The problem is described below. If anyone can help, that would be fantastic. – B. Stackhouse Feb 27 '23 at 17:46
  • @B.Stackhouse That's because this answer is dated 2018. – CJK Mar 08 '23 at 05:10
  • Might be worth updating the JavaScript to make it work in current versions of macOS. – nohillside Mar 10 '23 at 07:02
  • @nohillside To what end ? No one who submits an answer should be expected to provide revisions year-upon-year to ensure the code remains up-to-date. This isn't GitHub. I'm happy for someone who post a similar question looking for a solution that's either current or specific to a particular OS version, but I won't be maintaining code (doing so would also have the side-effect of invalidating it as an answer with respect to its original context—in this case, the context being the year 2018, which corresponds to maOS Mojave). – CJK Apr 05 '23 at 21:24
  • I agree that this is not GitHub and nobody is expected to continuously update their answer to keep it current. But there are comments indicating that people still find your answer via Google etc even though it doesn‘t work anymore. So there seems to be an interest in seeing it updated to work in current versions of macOS. This will help to keep the answer valuable and useful. – nohillside Apr 05 '23 at 21:41
  • PS: Nobody will complain if the answer contains two code snippets afterwards, one for Mojave and one for Ventura. – nohillside Apr 05 '23 at 21:42
  • Also, a new question was asked (https://apple.stackexchange.com/questions/456950/obtain-list-of-applications-that-can-open-a-file-via-terminal) and answered with a Swift solution. It got merged into this Q here because it basically is the same question still. – nohillside Apr 05 '23 at 21:44