Is there a macOS equivalent of the Windows assoc command?

Solution 1:

Changing the file association for a single file or a set of files (AppleScript)

This script demonstrates first that the default application for some text file on my system was set to TextEdit. Then it changes the file association for that particular file so it now opens with Atom. Finally, it associates all text files on the desktop with the Atom application.

tell application "System Events"
    get the default application of the file "/path/to/some file.txt"
        --> alias "Macintosh HD:Applications:TextEdit.app:" of application "System Events"

    # Individual file:
    set the default application of the file "/path/to/some file.txt" to ¬
        the path to the application named "Atom"

    # A set of files:
    set the default application of every file of the desktop folder whose ¬
        name extension = "txt" to the path to the application "Atom"
end tell

Changing the file associations for all files of a given type (JXA)

Using JavaScript for Automation, you can implement Core Foundation functions in a way you cannot do with AppleScriptObjC, so as to interact with Launch Services at the system level and change the file association for a given file type.

Here, I've targetted plain text files (these have extension .txt by default), and switched the default application that responds to them to Atom:

ObjC.import('CoreServices');

var contentType = 'public.plain-text';
var bundleID = Application('Atom').id();

$.LSSetDefaultRoleHandlerForContentType(
            contentType, 
            $.kLSRolesAll, 
            bundleID
        );

The file type must be targeted by way of a Uniform Type Identifier. These are special text strings that uniquely identify a given class or type of item. The link will take you to a page that lists Apple's system-declared UTIs for all the likely file types that you'll be interested in. Be careful not to simply choose the one that appears to match your needs at first glance, as UTIs are structured in a kind of inheritance tree. Therefore, I could have lazily picked out public.text, which I saw first in the list, until on further reading, we learn that this UTI is a base type for all text, which would include HTML and RTF files.

Solution 2:

No direct equivalent to assoc exists. Apple file mapping is implemented very differently than Windows. The file metadata has many variables that affect launch services and mapping a particular file to a particular app can involve several factors.

The macOS system uses a Launch Services database to map file types and secondarily, file extensions to applications that open them, the closest analog to assoc would be lsregister - a utility that exposes the database and API to the command line.

It's buried away inside the system folder, Core Services, framework that contains the LaunchServices.

There are tons of good questions and answers here once you know the utility to search for as are there tons of excellent blog posts, developer guides and official documentation.

Since I have /usr/local/bin in my path, I usually sym link it there so I can host type it:

mac:~ me$ ln -s /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister  /usr/local/bin
mac:~ me$ lsregister
lsregister: [OPTIONS] [ <path>... ]
                      [ -apps <domain>[,domain]... ]
                      [ -libs <domain>[,domain]... ]
                      [ -all  <domain>[,domain]... ]

Paths are searched for applications to register with the Launch Service database.
Valid domains are "system", "local", "network" and "user". Domains can also
be specified using only the first letter.

  -kill     Reset the Launch Services database before doing anything else
  -seed     If database isn't seeded, scan default locations for applications and libraries to register
  -lint     Print information about plist errors while registering bundles
  -lazy n   Sleep for n seconds before registering/scanning
  -r        Recursive directory scan, do not recurse into packages or invisible directories
  -R        Recursive directory scan, descending into packages and invisible directories
  -f        force-update registration even if mod date is unchanged
  -u        unregister instead of register
  -v        Display progress information
  -gc       Garbage collect old data and compact the database
  -dump     Display full database contents after registration
  -h        Display this help