5

I have a new Macbook Pro and I'm reminded that my development environment isn't tailored to what I setup on my previous Macbook. I was curious if it's possible to create a script in either AppleScript or Bash that I can run which I can define what file format should be designates as the Open with application, for all applications I usually use.

For example, when a file is right clicked, Get Info, Open with for HTML it's set as Safari. I know I can manually go to Open With and choose Visual Studio Code but I'd like to write something that will change all files throughout my system.

Is this possible? How do you target the Open With application?

2
  • Comments are not for extended discussion; this conversation has been moved to chat.
    – nohillside
    Commented Aug 10, 2019 at 5:16
  • 2
    Please edit any question clarifications elicited in comments back into the question to make sure that future visitors see all the relevant details directly in the question.
    – nohillside
    Commented Aug 10, 2019 at 5:35

6 Answers 6

4

As far as I know, all file associations are defined on the launchservices.secure.plist file.

For macOS Mojave/Catalina this file is located at:

~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist

You can read the file contents using:

defaults read ~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist

This command will output on terminal the file contents, which has each associated application with the corresponding file types.

Sample output:

$ defaults read ~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist

{
    LSHandlers =     (
                {
            LSHandlerContentType = "public.3gpp";
            LSHandlerPreferredVersions =             {
                LSHandlerRoleAll = "-";
            };
            LSHandlerRoleAll = "com.colliderli.iina";
        },
                {
            LSHandlerContentType = "org.7-zip.7-zip-archive";
            LSHandlerPreferredVersions =             {
                LSHandlerRoleViewer = "-";
            };
            LSHandlerRoleViewer = "com.aone.keka";
        },
                {
            LSHandlerPreferredVersions =             {
                LSHandlerRoleAll = "-";
            };
            LSHandlerRoleAll = "com.apple.dt.xcode";
            LSHandlerURLScheme = xcpref;
        },
                {
            LSHandlerContentType = "public.html";
            LSHandlerPreferredVersions =             {
                LSHandlerRoleAll = "-";
            };
            LSHandlerRoleAll = "com.apple.safari";
        }
    );
}

I cannot fully answer your question now, but this is the starting point to look at and understand how those parameters works, and after testing and figuring it out we can expand this information with complete details and some examples of edited working versions.


But for the specific situation, considering the case of making a kind of template to just to replicate, (or to have it stored as a backup):

I suggest you could manually customize one system the way you want it, by traditional Finder file associations methods, and once it is fine, you save that .plist file, then you can replace that .plist file on the new system, or on the new user profile, and [reboot?, logout?, or kill finder?] and login again, and this should be enough for the associations be replicated and ready.

[Comments and edits are welcome]

1
  • There is lot of information in the defaults man page. Especially the defaults write cmd and how and why the prefs are cached. Commented Aug 9, 2019 at 21:17
2

There is a command line application that can do this for you, it is called Duti and it is availible at https://github.com/moretension/duti . I compiled a version of Duti using Macports, and then dropped that utility in the Resources folder inside a script application wrapper, and call it using a "do shell script". I have tried it on multiple machines at work and it works every time!.

Here is my code for using Duti:

try
    tell application "Finder"
        set icon_path to (container of (path to me) as alias)
        set temp_path to POSIX path of (container of (path to me) as alias)
        set current_path to temp_path & "ChangeDefaultAdobeApps.app/Contents/Resources/"
        set iconPath to (icon_path & "ChangeDefaultAdobeApps.app:Contents:Resources:adobe_apps_software_icon_512.icns" as string) as alias
    end tell

    set jpgScript to current_path & "duti" & space & " -s com.adobe.Photoshop public.jpeg all"
    do shell script jpgScript
    set pdfScript to current_path & "duti" & space & "-s com.adobe.Acrobat.Pro com.adobe.pdf all"
    do shell script pdfScript
    set tiffScript to current_path & "duti" & space & " -s com.adobe.Photoshop public.tiff all"
    do shell script tiffScript
    display dialog "Settings Changed!" buttons {"OK"} default button "OK" with icon iconPath
on error
    display dialog "Something Went Wrong" buttons {"Cancel"} default button "Cancel" with icon 0
end try
1
  • 1
    Please forgive the way I make my "do shell script" commands, I know it is not the right way, but I like to have the command fully structured before I actually call it. That way I can stop the script before it executes the actual "do shell script" and take my constructed command, paste it into Terminal and see what it is doing (or in most cases when I do this, not doing). So it is just my lazy way of constructing "do shell script" scripts. Commented Aug 10, 2019 at 3:19
1

An application registers the types of items that it can handle by putting entries into its Info.plist. The CFBundleDocumentTypes key is an array of dictionary entries describing the document types (name, icon, role, etc - see the Information Property List Key Reference). For example, an application I use to provide a couple of custom document icons has these entries:

<key>CFBundleDocumentTypes</key>
<array>
    <dict>
        <key>CFBundleTypeIconFile</key>
        <string>ruby-script_Icon</string>
        <key>CFBundleTypeName</key>
        <string>Ruby Source</string>
        <key>CFBundleTypeRole</key>
        <string>None</string>
        <key>LSItemContentTypes</key>
        <array>
            <string>public.ruby-script</string>
        </array>
    </dict>
    <dict>
        <key>CFBundleTypeIconFile</key>
        <string>property-list_Icon</string>
        <key>CFBundleTypeName</key>
        <string>Property List</string>
        <key>CFBundleTypeRole</key>
        <string>None</string>
        <key>LSItemContentTypes</key>
        <array>
            <string>com.apple.property-list</string>
        </array>
    </dict>
</array>

The app is just a proxy for the target application and is created by copying the following script into the Script Editor and saving it as an application, then copying the icon resources and editing its Info.plist. It doesn’t really do anything but open files in the application I want (BBEdit in this case), but you could also perform other setup routines:

on open droppedItems
    -- perform setup or whatever
    set args to ""
    repeat with anItem in droppedItems
        set args to args & quoted form of (POSIX path of anItem) & " "
    end repeat
    do shell script "open -b com.barebones.bbedit " & args
end open

From there, I just use Finder’s Get Info window to make the proxy app the default application for those file types. Using this approach I can assign custom icons, with double-click and drag operations that open the document(s) as usual, without having to alter the target application.

1

I usually avoid GUI scripting like it's the plague, but for this project I had to resort to GUI scripting in the second half of the code. Save this following AppleScript code as an application. Double-clicking this app's icon in Finder will do nothing. In short, any file you want to change its default application for, of those file types, just drag and drop that file onto this Droplet's icon and you will be given the option to choose a new application as the default application for those file types.

This AppleScript code works for me using the latest version of macOS Mojave.

on open of droppedItems
    --  Executed when files are dropped on the script
    repeat with thisFile in droppedItems
        activate
        set chosenApp to (choose application with prompt ¬
            "Choose  Your Preferred Default Application" as alias)
        tell application "System Events"
            set default application of thisFile to chosenApp
            set fileName to name of thisFile
        end tell
        tell application "Finder"
            if locked of thisFile is true then set locked of thisFile to false
            activate
            repeat until frontmost
                delay 0.1
            end repeat
            reveal thisFile
        end tell
        delay 0.1
        tell application "System Events"
            keystroke "i" using {command down}
            repeat until button "Change All…" of scroll area 1 of window ((fileName & " Info") as text) of application process "Finder" exists
                delay 0.2
            end repeat
            delay 0.1
            click button "Change All…" of scroll area 1 of window ((fileName & " Info") as text) of application process "Finder"
            delay 0.2
            if exists of button "Continue" of window 1 of application process "Finder" then
                --click button "Continue" of window 1 of application process "Finder"
                keystroke return
                delay 0.1
                keystroke "w" using {command down}
            else
                keystroke "w" using {command down}
            end if
        end tell
    end repeat
end open

This following animation is an example of me dragging a .png on to the AppleScript Droplet's icon. The default application to open .png files on my computer was Preview.app ad by dragging a .png on to the AppleScript Droplet's icon... I was able to change the default application to open ALL .png files to Photoshop

enter image description here

You may need to change the delay values in the code, to function correctly on your system.

Don't forget to grant access permissions in System Preferences to allow your applet to control your system.

enter image description here

enter image description here

enter image description here

2
  • Wow, that is a nice! Unfortunately I tried it on Catalina without luck, still don't know why. I tried the same: from preview to photoshop: but when the Info window appears, the app it shows is not the one (photoshop) that I selected on the script, but still is the original app (preview.app). Weird is the fact that "change all" is active, when it should be grayed-out, since no changes are being made; once clicked, the popup tells "are you sure...change all similar... .png to preview.app". Well, Catalina is beta, I'm not complaining, this is to inform you about it, because I like your answer.
    – Prado
    Commented Aug 10, 2019 at 12:28
  • @Prado I ran into the same issues at first also. I was able to solve it by making sure Finder,System Events, and Change Default App.app were all granted privileges to control the computer in the System Preferences. I just added images to my post showing this.
    – wch1zpink
    Commented Aug 12, 2019 at 15:57
1

You can check out my answer here to a similar question.

The salient, script-portion of it is a JavaScript For Automation (JXA) script that can change the default application for all files of a specified uniform type identifier. It can be run in Script Editor by changing the language option in the navigation bar at the top of the window from "AppleScript""JavaScript".

ObjC.import('CoreServices');

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

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

Of course, you can also run it from within AppleScript using:

run script "<code>" in "JavaScript"

The above script used "as is" would set the application Atom (an open-source text editor) as the default application used to open/view/edit plain text files. This roughly translates to files with extension ".txt".

The link to the original answer contains more details and useful reading references to understand more about UTIs if you aren't already familiar with them.

2
  • getting a -50 error when trying to run this JXA code. Think it's a permissions issue on Catalina. Any tips?
    – luckman212
    Commented May 7, 2020 at 3:17
  • Have you added Script Editor or whichever program you use to execute the script to the appropriate permissions settings ?
    – CJK
    Commented May 8, 2020 at 1:18
1

I would recommend SwiftDefaultApps which is described as "Replacement for RCDefaultApps, written in Swift".

(Longtime Mac users will remember RCDefaultApps as a preference pane which could be used to set default apps for certain types of files.)

What makes SwiftDefaultApps the better solution, IMO, is that most of the other suggestions seem very complicated and involve a lot of scripting. SwiftDefaultApps not only gives you a GUI way of making these changes without going through the "Get Info" panel, but it also has a command-line tool which is much simpler than the other solutions posted so far.

If you download the latest release (currently SwiftDefaultApps-v2.0.1.zip) you will find a preference pane and (this is the important part) a command-line tool named swda (which stands for [sw]ift [d]efault [a]pps, of course).

The preference pane is a very easy GUI-based way to set your defaults without having to go through "Get Info" again and again, but the swda tool is what you are looking for.

Run swda with no arguments to get a basic "usage" prompt:

Utility to retrieve and manipulate default applications in macOS.

Available commands:
- getHandler  Returns the default application registered for the URI Scheme or <subtype> you specify..
- getApps     Returns a list of all registered applications.
- getSchemes  Returns a list of all known URI schemes, accompanied by their default handler.
- getUTIs     Returns a list of all known UTIs, and their default handler.
- setHandler  Sets <application> as the default handler for a given <type>/<subtype> combination.
- help        Prints this help information
- version     Prints the current version of this app

Use swda foo to get more information on how to use foo such as swda setHandler.

With swda, I can see which kinds of files are set to use TextEdit (instead of BBEdit )

swda getUTIs | fgrep -i textedit

or, worse, Xcode:

swda getUTIs | fgrep -i Xcode

Nothing worse than double-clicking a .sh file expecting it to open in BBEdit, only to see Xcode launch. 😮

For example:

swda setHandler --UTI public.shell-script --app /Applications/BBEdit.app

results in:

SwiftDefaultApps SUCCESS: Default handler has succesfully changed to com.barebones.bbedit

It may take a little time to go through all the different settings, but I believe that swda is the easiest way to accomplish what you're looking for, and once you have things set the way that you want them, it should be easy to write a small shell script to replicate those choices on a new Mac.

1
  • I think a claim that swda setHandler --UTI public.shell-script --app /Applications/BBEdit.app is better and less complicated than $.LSSetDefaultRoleHandlerForContentType("public.shell-script", $.kLSRolesAll, "com.barebones.bbedit"); is perhaps a bit of an embellishment. My script and your third-party tool appear to do the same thing when it comes to setting the default application for a type, and the script is literally one line long (the others just being present for clarity, but not functionally necessary). All the other functions are achievable with similar one-line ObjC calls.
    – CJK
    Commented Aug 19, 2019 at 8:44

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .