
I often
ssh from one Mac to another in order to manage applications on the remote machine. Thus, I find it frustrating that Mac OS X includes no easy way to quit an application politely (allowing the app to save data, clean up after itself, etc.) from the command line. There are command line options that are easy to use, but not nice to applications, and an option that is nice to applications, but not easy to use:
- Kill and killall are easy to use, but none of their possible signals will initiate a polite quit of an OS X application. All kill signals either force a quit without saving, or (worse) force a quit without saving and with an error message from the OS. This is because kill deals with processes, not applications.
- Osascript can use an Applescript command to initiate a polite quit using the form osascript -e "tell application "AppName" to quit". But it is tedious to write that command on the command line, especially if one has to do this sort of thing more than once in a blue moon.
So I made a way. Now I just type
quit AppName on the command line, and the app quits nicely. The
quit tool is a
bash script, as follows:
#!/bin/bash
#-----------------------------------------------------------
#
# Quit: quits Mac OS X applications politely
#
# Written by Jon Stovell, July 11, 2009
#
#-----------------------------------------------------------
#-----------------------------------------------------------
# This script takes one or more application names as
# arguments, and uses osascript to tell each one to quit.
# Unlike kill and killall, this allows applications to save
# files and perform any necessary operations before exiting.
#
# This script is not case sensitive.
#
# Note: application names are NOT process names! The
# application name is the name that Script Editor uses.
# Often the process name and the application name are
# identical, but not always.
#-----------------------------------------------------------
usage()
{
echo "Usage: `basename $0` [-a] [-p] Application1 \"Application 2\" ..."
echo ""
echo "Arguments are the names of one or more applications."
echo "Arguments are not case sensitive."
echo "Arguments with spaces should be quoted."
echo ""
echo "Options:"
echo " -a Match argument string with any of the application's"
echo " name, short name, title, or display name."
echo " E.g.: \`quit \"Microsoft Word\"\` and \`quit -a Word\`"
echo " will both quit Microsoft Word, because the app calls"
echo " itself \"Word\" in the menu bar."
echo " -p Use partial matches (e.g. edit for TextEdit). Prompts"
echo " for confirmation."
echo ""
exit 65
}
searchall=1
is_contains="is"
while getopts "ap" opt
do
case $opt in
a) searchall=0 ;;
p) is_contains="contains" ;;
[?]) usage; exit ;;
esac
shift $(($OPTIND - 1)) # Decrements the argument pointer so it points to next argument. $1 now references the first non option item supplied on the command-line if one exists.
done
if [ -z $1 ]; then usage; fi
for arg in "$@"
do
if [ searchall=0 ]
then
appname=`osascript -e "tell application \"System Events\" to return every application process whose (name $is_contains \"$arg\" or short name $is_contains \"$arg\" or title $is_contains \"$arg\" or displayed name $is_contains \"$arg\")"`
else
appname=`osascript -e "tell application \"System Events\" to return every application process whose name $is_contains \"$arg\""`
fi
if [[ -n $appname && $appname != *", "* ]] # found 1 matching application
then
if [[ $is_contains == is ]]
then
osascript -e "ignoring application responses" -e "tell application \"$appname\" to quit with saving" -e "end ignoring"
else
echo "Choose the application to quit:"
eval set $appname # this allows multi-word selections in select
select appname in "$@"
do
osascript -e "ignoring application responses" -e "tell application \"$appname\" to quit with saving" -e "end ignoring"
break
done
fi
elif [[ -z $appname ]] # found no matching application
then
echo "No running application matches \"$arg\""
elif [[ $appname == *", "* ]] # found >1 matching applications.
then
appname=`echo $appname | sed -e 's/^/\"/' -e 's/$/\"/' -e 's/, /\" \"/'`
echo "\"$arg\" matches multiple applications."
echo "Choose the application to quit:"
eval set $appname # this allows multi-word selections in select
select appname in "$@"
do
osascript -e "ignoring application responses" -e "tell application \"$appname\" to quit with saving" -e "end ignoring"
break
done
fi
done
To use the above code, you'll need to copy, paste, save, and make executable ... or you can simply
grab quit from MacUpdate and drag it to its final destination.
This script essentially executes the
osascript command, but without requiring all the verbiage. Originally I tried just putting a one line addition into
~/.profile to do this, but ran into some limitations that made a full-fledged bash script more appropriate.
This script takes one or more application names as arguments, and uses
osascript to tell each one to quit. The arguments are not case sensitive, so
quit textedit will work. App names with spaces should be quoted or have the spaces escaped. If more than one matching application is found, the user will be prompted to select the correct one from a list. This prompt always occurs with the
-p option (see below). The program has two options:
- -a will match with all possible versions of an app's name, including its name, short name, title, or display name. (e.g. quit "Microsoft Word" and quit -a Word will both quit Microsoft Word, because the app calls itself "Word" in the menu bar.
- -p tells the script to use partial matches to find the application to quit. This option always prompts for confirmation before quitting the app. (e.g. if TextEdit is running, quit -p edit will prompt the user whether they want to quit TextEdit.)
[
robg adds: This is a more-thorough and enhanced alternative to a simple script that was
posted here years ago. Note that this version only works in 10.5, and the MacUpdate link will always have the newest code, so that's your best bet. I mirrored the code here just in case the original ever vanishes.]