-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathquit
executable file
·240 lines (213 loc) · 8.66 KB
/
quit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
#!/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] [-s|n] <application names>"
echo
echo "Arguments are the names of one or more applications, or \`all\`"
echo "to quit all foreground applications except Finder and Terminal."
echo "Arguments are not case sensitive."
echo "Arguments with spaces should be quoted."
echo "Arguments with spaces should be quoted."
echo
echo "Options:"
echo " -a Match argument string with any of the application's name, "
echo " displayed name, short name, or title. E.g.: \`quit \"microsoft word\"\` "
echo " and \`quit -a word\` will both quit Microsoft Word, because"
echo " the app calls itself \"Word\" in the menu bar."
echo " -p Use partial matches (e.g. edit for TextEdit)."
echo " Prompts for confirmation."
echo " -s Attempt to save modified files when quitting."
echo " Overrides any previous -n option."
echo " -n Do not attempt to save modified files when quitting."
echo " Overrides any previous -s option."
echo
echo "If neither the -s or -n options are specified, `basename "$0"` will try"
echo "to determine if any documents require saving, and if necessary"
echo "will ask the user whether to save or not."
exit 65
}
tellToQuit()
{
runningApps=`osascript -e "tell application \"System Events\" to count (application processes whose displayed name is \"$appname\")"`
osascript -e "ignoring application responses" -e "tell application \"$appname\" to quit $saveAction" -e "end ignoring"
sleep 1
waitCount=0
unscriptableWarningShown=false
until [[ `osascript -e "tell application \"System Events\" to count (application processes whose displayed name is \"$appname\")"` -lt $runningApps || $waitCount -eq 10 ]]
do
if [[ $scriptable == false && $unscriptableWarningShown == false ]]
then
echo "$appname does not respond to standard scripting terminology." 1>&2
echo "If it has unsaved changes, it may not quit." 1>&2
unscriptableWarningShown=true
fi
if [[ $saveAction == "saving yes" ]]; then saveUntitled; fi
sleep 1
waitCount=$(( $waitCount + 1 ))
if [[ $waitCount -ne 10 ]]
then
printf . 1>&2
else
printf . 1>&2
sleep 1
echo 1>&2; echo "$appname has not quit. Give up?" 1>&2
read -n 1 -p "(y/n): "; echo 1>&2
if [[ $REPLY != y ]]; then waitCount=0; fi
fi
done
if [[ $waitCount -gt 0 && $waitCount -ne 10 ]]; then echo 1>&2; fi
if [[ $scriptable == false && $unscriptableWarningShown == true && `osascript -e "tell application \"System Events\" to count (application processes whose displayed name is \"$appname\")"` -lt $runningApps ]]; then echo "$appname has quit." 1>&2; fi
}
saveUntitled()
{
newdocs=$(osascript -e 'try' -e 'set filePath to "~/Desktop"' -e 'set newdocs to 0' -e 'tell application "System Events"' -e "set theApplication to application process \"$appname\"" -e 'set targetapp to item 1 of theApplication' -e 'set targetwindow to a reference to front window of targetapp' -e 'repeat while (targetwindow exists) and ((count sheets of targetwindow) > 0)' -e 'set targetsheet to front sheet of targetwindow' -e 'get name of every button of targetsheet' -e 'if result contains "Save…" then' -e 'click button "Save…" of targetsheet' -e 'else if result contains "Save" then' -e 'set newdoc to filePath & "/Untitled " & date string of (current date) & space & time of (current date)' -e 'set frontmost of targetapp to true' -e 'delay 0.1' -e 'tell targetsheet to keystroke "g" using {command down, shift down}' -e 'delay 0.1' -e 'try' -e 'set value of text field 1 to newdoc' -e 'delay 0.5' -e 'click button "Go"' -e 'on error theError' -e 'keystroke newdoc' -e 'delay 1' -e 'keystroke return' -e 'end try' -e '' -e 'click button "Save" of targetsheet' -e 'set newdocs to newdocs + 1' -e 'else' -e 'exit repeat' -e 'end if' -e 'delay 0.5' -e 'end repeat' -e 'end tell' -e 'return newdocs' -e 'end try')
if [[ -n $newdocs && $newdocs -gt 0 ]]; then echo "$newdocs new file(s) have been saved to the desktop."; fi
}
promptForApp()
{
appname=`echo $appname | sed -e 's/^/\"/' -e 's/$/\"/' -e 's/, /\" \"/'`
echo "Choose the application to quit (Press Ctrl-C to cancel):" 1>&2
eval set $appname # this allows multi-word selections in select
select appname in "$@"
do
if [[ $appname == "" ]]
then
echo "Please select by number." 1>&2
else
break
fi
done
}
checkIfScriptable()
{
# scriptable will be a string, either "true" or "false"
scriptable=`osascript -e "tell application \"System Events\" to return has scripting terminology of first application process whose displayed name is \"$appname\""`
}
setSaveAction()
{
if [[ $saveAction == "" ]]
then
checkForModifiedDocuments
if [[ $needToSave -ge 1 ]] || [[ $needToSave -eq -1 && $scriptable == true ]]
then
promptForSaveAction
fi
fi
}
checkForModifiedDocuments()
{
# needToSave will be an integer. It will be -1 if there was an error.
needToSave=`osascript -e "try" -e "tell application \"$appname\" to count (get its documents whose modified is true)" -e "on error" -e "try" -e "tell application \"$appname\" to count (get its documents whose saved is false)" -e "on error" -e "-1" -e "end try" -e "end try"`
}
promptForSaveAction()
{
if [[ $needToSave -gt 1 ]]
then
echo "Save changes to $needToSave modified documents in $appname? (Press Ctrl-C to cancel)" 1>&2
read -n 1 -p "(y/n): "; echo
elif [[ $needToSave -eq 1 ]]
then
echo "Save changes to $needToSave modified document in $appname? (Press Ctrl-C to cancel)" 1>&2
read -n 1 -p "(y/n): "; echo
elif [[ $needToSave -eq -1 ]]
then
echo -e "Save changes to modified documents (if any) in $appname? (Press Ctrl-C to cancel)" 1>&2
read -n 1 -p "(y/n): "; echo
fi
if [[ -n $REPLY && $REPLY == y ]]
then
saveAction="saving yes"
elif [[ -n $REPLY && $REPLY == n ]]
then
saveAction="saving no"
elif [[ -n $REPLY ]]
then
echo "Invalid response. Aborted." 1>&2
exit
else
exit
fi
}
searchall="false"
is_contains="is"
saveAction=""
while getopts "apns" opt
do
case $opt in
a) searchall="true" ;;
p) is_contains="contains" ;;
n) saveAction="saving no" ;;
s) saveAction="saving yes" ;;
[?]) usage ;;
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
if [[ $1 == "all" ]]
then
allapps=`osascript -e "tell application \"System Events\" to return displayed name of every application process whose (background only is false and displayed name is not \"Finder\" and displayed name is not \"Terminal\" and displayed name is not \"iTerm\" and displayed name is not \"Path Finder\")"`
allapps=`echo $allapps | sed -e 's/^/\"/' -e 's/$/\"/' -e 's/, /\" \"/g'`
eval set $allapps
for appname in "$@"
do
tempSaveAction=$saveAction
checkIfScriptable
setSaveAction
tellToQuit
saveAction=$tempSaveAction
done
else
for arg in "$@"
do
if [[ $searchall == "true" ]]
then
appname=`osascript -e "tell application \"System Events\" to return displayed name of every application process whose (displayed name $is_contains \"$arg\" or name $is_contains \"$arg\" or short name $is_contains \"$arg\" or title $is_contains \"$arg\")"`
else
appname=`osascript -e "tell application \"System Events\" to return displayed name of every application process whose displayed name $is_contains \"$arg\""`
fi
if [[ -n $appname && $appname != *", "* ]] # found 1 matching application
then
if [[ $is_contains == is ]]
then
checkIfScriptable
setSaveAction
tellToQuit
else
promptForApp
checkIfScriptable
setSaveAction
tellToQuit
fi
elif [[ -z $appname ]] # found no matching application
then
echo "No running application matches \"$arg\"" 1>&2
elif [[ $appname == *", "* ]] # found >1 matching applications.
then
echo "\"$arg\" matches multiple applications." 1>&2
promptForApp
checkIfScriptable
setSaveAction
tellToQuit
fi
done
fi