On occasion, one needs to force-quit a runaway Applescript application, for example, an application caught in an endless loop, or a stay-open application that did not properly quit itself. The following do shell script command, which should be placed at the start of an Applescript application’s execution code, creates a shell script that runs in the background and performs two tasks:
(1) force-quits the Applescript application in which it is embedded if the application is still running after forceQuitTime seconds
(2) quits itself if it finds that the Applescript application is no longer running upon repeated testing of the application’s run status every queryCycleTime seconds
The user provides two variable values for the do shell script command:
forceQuitTime
- the number of seconds the background shell script should wait before force-quitting the Applescript application
queryCycleTime
- the number of seconds the background shell script should wait between repeated checks of the Applescript application’s run status to determine if the application is no longer running and thus to signal the shell script to quit itself
set {forceQuitTime, queryCycleTime} to {3600, 60} -- the user sets these two values
do shell script "(" & ¬
"processName=$(defaults read " & ((path to me as text) & "Contents:Info.plist")'s POSIX path's quoted form & " CFBundleExecutable)" & linefeed & ¬
"pid=$(pgrep -nx \"$processName\")" & linefeed & ¬
"launchTime=$(ps -p $pid -o start=)" & linefeed & ¬
"while [[ SECONDS -lt " & forceQuitTime & " ]]; do" & linefeed & ¬
" [[ \"$launchTime\" = $(ps -p $pid -o start=) ]] && : || exit 0" & linefeed & ¬
" timeRemaining=$(( " & forceQuitTime & " - SECONDS ))" & linefeed & ¬
" [[ timeRemaining -lt " & queryCycleTime & " ]] && sleep $timeRemaining || sleep " & queryCycleTime & linefeed & ¬
"done" & linefeed & ¬
"[[ \"$launchTime\" = $(ps -p $pid -o start=) ]] && kill -9 $pid || :" & ¬
") &>/dev/null &" -- this statement is placed without modification at the start of the application's execution code
-- The user may supply any desired number of seconds for forceQuitTime and queryCycleTime. In the current assignment statement, 3600 signifies that the shell script will force-quit the Applescript application if still running after 1 hour (3600 seconds), and 60 signifies that the shell script will test the application's run status every minute (60 seconds) and quit itself if it finds that the application is no longer running
Explanatory notes:
initial “(” and final “) &>/dev/null &”
- Runs the shell script in the background and executes the script’s commands as a subprocess
- Both features are necessary for pgrep to be able to recognize the current process
“processName=$(defaults …”
- Gets the Applescript application’s process name from the Info.plist file’s CFBundleExecutable property
“pid=$(pgrep …”
- Gets the Applescript application’s Unix process ID number by providing pgrep with the process name
- The pgrep command’s -n option restricts matching only to the newest process with the Applescript application’s process name
and prevents pgrep from matching an older application with an identical process name
- The “do shell script” command should be placed at the start of the application’s execution code to make vanishingly
small the chance of pgrep matching an identically named process even newer than the current application
“launchTime=$(ps -p …”
- Gets the clock time the Applescript application was launched, e.g., “10:17AM”
- This value acts an additional means besides the Unix process ID number of correctly identifying the current application and
serves as a safeguard against the shell script force-quitting another application with the same Unix process ID number
as might otherwise occur if, for instance, the user specified a very long value for forceQuitTime (days to weeks)
“while [[ SECONDS -lt …”
- Sets up a while loop that repeatedly checks if the application is still running
- Uses the special variable SECONDS, which contains the shell script’s execution time in seconds
" [[ "$launchTime" = …"
- Tests if the Applescript application is still running; if not, causes the background shell script to quit itself (“exit 0”)
- Uses two criteria to correctly identify the Applescript application: Unix process ID number (“-p “$pid”) and
launch time (”"$launchTime" = " … “-o start=”)
" timeRemaining=$(( …" and " [[ timeRemaining …"
- Pauses the background shell script for queryCycleTime seconds before the next while loop execution
- If the time remaining before force-quitting is less than queryCycleTime, uses the former value for the pause time rather
than queryCycleTime
“[[ "$launchTime" = …”
- Tests if the Applescript application is still running after forecQuitTime seconds and, if so, force-quits it (“kill -9 $pid”)
Edit note: A typo was corrected on line 7 of the shell script.