MacOS – How to create an apple script that toggles between opening and closing an application

applescriptmacosterminal

I would like to create an executable file that opens an application if it is not currently open, and quits the application if it is already open.

I would also like the window of the application to be hidden after it opens.

I already figured out that the terminal command osascript -e 'tell application "Finder"' -e 'set visible of process "KeyboardCleanTool" to false' -e 'end tell'

will hide an application after it is open, so this command would also need to be integrated into the executable file.

The application is called KeyboardCleanTool and is stored at /Applications/KeyboardCleanTool.app

(macOS Catalina)

Best Answer

You would be best advised to either create the application from within Script Editor as an actual AppleScript app written in AppleScript; or to create an executable shell script program that is written in shell script without calling out to osascript. There's nothing wrong per se about mixing it up, and indeed there's lots of situations where it's needed or useful, but this isn't one as both scripting languages have their own dedicated mechanisms for doing what you need. I'll show you both example scripts:

AppleScript

First, I'd recommend running a quick line of AppleScript to obtain the bundle identifier of KeyboardCleanTool, which is one way to allow AppleScript to reference an application without having that application launch as a result without an explicit request for it to do so (some applications do this because their AppleScript dictionary only avails itself to be read by AppleScript if the application is running).

So, run this line, then you can dispose of it after taking a note of the result:

return the id of the application named "KeyboardCleanTool"

It'll probably be something like "com.hegenberg.KeyboardCleanTool". That's what I'll use in the script below, which you can replace with the correct bundle identifier:

property id : "com.hegenberg.KeyboardCleanTool"
property A : a reference to application id id

if A is running then return quit A
run A

There are three ways to open applications using AppleScript, each with different results:

  • activate opens the application, brings it to the foreground, and gives it focus;
  • launch opens the application and reveals the windows but doesn't bring them into the foreground, nor gives it focus. The windows will be obscured behind whatever application currently has focus, and possibly others (I don't recall fully, but the newly launched app might sit at the back of the application z-ordering);
  • run opens the application but neither shows its windows nor gives it focus. For applications that have neither a dock nor menu bar icon, you won't be given any signal that it has been opened. This is the command it sounds like you want.

Using tell application "Finder" to set visible of process "KeyboardCleanTool" to false is not a great method. Firstly, you shouldn't use Finder for controlling the GUI, which is what this is doing; use System Events, which was created specifically to take the job away from Finder because Finder can't even do its own job properly without having a melt down, let alone trying to prioritise calls to manipulate the UI.

It also requires additional permissions that I'm sure you've already granted, but is worth mentioning for other users who don't wish to do this.

Lastly, if you don't need call in outside help to do something, it's much more advantageous (quicker, more efficient, less demanding on resources) to use a means already availed to you by the core scripting commands.

To create an Application using the above script: Save the script in Script Editor, selecting the type from the drop down list in the Save dialog that is called something like "Application bundle" or "AppleScript Applet". You'll know it when you see the four options. It can be saved anywhere you like, but a sub-directory in your user applications folder would be a sensible choice, i.e. ~/Applications, or the root applications folder, /Applications if you prefer.

Shell Script, e.g. bash

Let me know if you use a shell other than bash, and I'm aware Catalina is packaged with zsh and I, myself, preferentially use Fish and recommend you check it out.

The bash script for the task at hand can be this:

pkill -i 'KeyboardCleanTool' || lsappinfo launch nofront=true async=true /Applications/KeyboardCleanTool.app

This can be saved as anything you like, and ideally in a directory that is listed in your $PATH environment variable, such as _/usr/local/bin. It will also need to be made executable. Taking the above script, here's the entire bash code that will do all this:

echo '#!/usr/bin/env bash

A="${1-KeyboardCleanTool}" # Default app name if none given
[[ -e "${fp="/Applications/$A.app"}" ]] || fp="$( locate -l 1 -i "*/$A.app" )"
pkill -i "$A" || lsappinfo launch nofront=true async=true "$fp"' > /usr/local/bin/rd; chmod +x /usr/local/bin/rd

Once that's done, you can invoke the program like this:

rd textedit           # Will run or quit TextEdit
rd keyboardcleantool  # Will run or quit KeyboardCleanTool
rd                    # (Default) Will run or quit KeyboardCleanTool

And, of course, you don't need to save the program in /usr/local/bin. Saving it in your main /Applications folder is perfectly fine. It will have an icon representing a Terminal shell, but you can treat it like any other application: double-click to run from within Finder and it will exit when it's done.