# Launching CLI apps from the macOS GUI

I spend most of my time working from the command-line, so being able to inspect an astronomical FITS format image in DS9 with ds9 -multiframe -zoom to fit ./thing.fits as fast as I can type it is useful to me. (Except, obviously, I have an alias for those options. I'm not an animal.)

SAO distributes X11 builds of DS9 for macOS, which I can launch from the command line, so I use those. Now, their "Aqua" builds of ds9 for macOS use the Tcl/Tk framework's implementation of the native macOS widgets (prettier!) and Finder double-click-ability (convenient!) at the cost of convenient command-line launching and, most recently, incompatibility with Dark Mode™ on macOS Monterey (disaster!).

So, I stick with the command line version. Sometimes, though, I want to open a FITS file by double-clicking it in the Finder. This is the hack I came up with to enable that.

# Step 0. Get the command-line (X11) version of ds9

Extract the file, and put the contents (both ds9 and ds9.zip, not any folder they extracted into) into /usr/local/bin. (The /usr/local/bin folder is hidden in the Finder, but you can type open /usr/local/bin in the terminal to open a window there.)

Verify this works first! That means opening a terminal window and typing ds9 and waiting to make sure XQuartz starts, and the ds9 window opens, and no major errors appear in your terminal window.

# Step 1. Open Script Editor

Only "applications" can open files in macOS, not dinky little "executables". So, we have to make an "application."

In your /Applications/Utilities folder, there's an app called "Script Editor". Open it up and paste this in:

on run
process({})
end run

on open dropped_files
process(dropped_files)
end open

on process(dropped_files)
set file_list to ""
repeat with file_path in dropped_files
set file_list to file_list & " " & quoted form of POSIX path of file_path
end repeat
do shell script "/usr/local/bin/ds9 -multiframe -zoom to fit " & file_list & " &> /dev/null &"
end process


You can tweak the options following the path to ds9 in the above do shell script line to be what you prefer. The ampersand (&) is the concatenation operator, so what we're doing is constructing a command just like the one we'd type in the terminal to launch ds9 with one or more files as arguments. The final &> /dev/null & backgrounds the process and indicates we don't care about its outputs at all, so that the script can exit without waiting for ds9. (Otherwise, you'd get an extra icon in the dock every time you launched it this way.)

Save as "File Format: Application" to create a .app bundle (I called mine ds9.app). Put it in /Applications, or where-ever you prefer.

# Step 2. Edit Info.plist

App bundles are just folders, so for the next trick open ds9.app/Contents/Info.plist in a text editor. You can control-click (or right-click, or two-finger-click) the ds9 app bundle icon and choose "Show contents" to see inside and get to Contents/Info.plist. Open it in your favorite text editor for code.

Right before the final </dict>, add:

    <key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>fits</string>
<string>fit</string>
</array>
<key>CFBundleTypeName</key>
<string>Flexible Image Transport System image</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSHandlerRank</key>
<string>Owner</string>
</dict>
</array>


Next, save the .plist file. You can check if everything's right using plutil, e.g.

\$ plutil ds9.app/Contents/Info.plist
ds9.app/Contents/Info.plist: OK


(Note: This edit should be sufficient to make the app a drop target for FITS files, but it seems like LaunchServices can get confused. You might need a magic incantation like /System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -kill -domain local -domain system -domain user to regenerate the list of file associations.)

Double-click your app bundle to launch an XQuartz version of ds9. It might take a second. (If you just installed XQuartz for the first time at the beginning of this guide, you may even need to reboot first.)

# Step 3. Update file associations (if needed)

There's probably a command-line way to do this, but right-clicking on a one .fit and one .fits file and choosing Get Info gets you the window shown below. Select your new .app under the "Open with:" section and click "Change All..." to get this prompt

Confirm by clicking "Continue," and we're done. Now, double-clicking any .fits or .fit file should open an XQuartz ds9 window. Dragging multiple files onto the icon will open them within the same window as separate frames.

# Thanks

I cobbled this together from a couple places: First, https://github.com/RichardBronosky/AppleScript-droplet, which has a more flexible solution for launching arbitrary scripts. Second, the bundle keys and values were taken from this StackOverflow answer, though it omits the fact you must include <key>CFBundleDocumentTypes</key> before the array.

Thanks to Meghan O'Brien and Avalon McLeod for feedback on an earlier version of this blog post!

posted

← return home
Background image of the Carina nebula by NASA, ESA, N. Smith (University of California, Berkeley), and The Hubble Heritage Team (STScI/AURA)