Take the 2-minute tour ×
Ask Different is a question and answer site for power users of Apple hardware and software. It's 100% free, no registration required.

I have the following workflow that I need to execute on a daily basis for a period of days:

automation workflow

The computers start at a specified time, Mathematica and a specific Mathematica notebook launch and evaluate. Then I need to quit Mathematica programmatically and let the relevant schedules shut down the computers. I've worked out everything here except the highlighted step to quit Mathematica with an Applescript application.

To start with I created a simple application using the Applescript Editor:

tell application "Mathematica" to quit

This will shut down Mathematica if the open notebook has been saved which the notebook does itself. Now I need to add a timer to the Applescript application.

An earlier question Automator workflow or AppleScript to close Safari after a period of time does something similar that I hoped to adapt to my needs. It shows the following code:

global quit_after, check_every

set quit_after to 2700
set check_every to 10
set minute to quit_after / 60

display dialog "Check is performed every " & check_every & " seconds. Things will be quit after " & minute & " minutesof system inactivity."

on reopen
    display dialog "Check is performed every " & check_every & " seconds. Things will be quit after " & minute & " minutes of system inactivity."
end reopen

on idle
    set idletime to do shell script "echo $((`ioreg -c IOHIDSystem | sed -e '/HIDIdleTime/ !{ d' -e 't' -e '}' -e 's/.* = //g' -e 'q'` / 1000000000))"
    if (idletime as integer) > quit_after then
        tell application "System Events"
            if ((name of processes) contains "Safari") then
                tell application "Safari" to quit
            end if
        end tell
    end if
    return check_every
end idle

I don't need the dialog boxes and I want it to close Mathematica after say 15 minutes, so I tried this as a next step towards a solution.

global quit_after, check_every

set quit_after to 900
set check_every to 10
set minute to quit_after / 60

on idle
    set idletime to do shell script "echo $((`ioreg -c IOHIDSystem | sed -e '/HIDIdleTime/ !{ d' -e 't' -e '}' -e 's/.* = //g' -e 'q'` / 1000000000))"
    if (idletime as integer) > quit_after then
        tell application "System Events"
            if ((name of processes) contains "Mathematica") then
                tell application "Mathematica" to quit
            end if
        end tell
    end if
    return check_every
end idle

It doesn't work.

So I have two questions, can anyone suggest:

  • How to get my modified code to work?
  • How to alter it so it will shut down Mathematica at a specified time on weekdays only?

The latter would work better for me as I think I'd have fewer conflicts if I shut down Mathematica at a specific time rather than after a certain amount of time has passed.

share|improve this question
    
I did find a work around the bares mention. Use my first Applescript (the shortest one), save it as a script rather than an app, then schedule it to run at specific times from iCal. Works great. No muss no fuss. Still I do want an Applescript solution so I can learn it better. –  Jagra Jul 18 '12 at 12:46

3 Answers 3

up vote 3 down vote accepted

Here's why it doesn't work.

HIDIdleTime is the number of seconds of inactivity, so unless the user do absolutely nothing during fifteen minutes, your script doesn't work.

Here's how to do this in AppleScript.

set timeToQuit to 50700 -- time in seconds =14h05
set currDate to current date

-- do nothing on Saturday and Sunday or this script is launched after 14h05
if weekday of currDate is in {Saturday, Sunday} or (time of currDate) > timeToQuit then return

delay (timeToQuit - (time of currDate)) -- wait 
tell application "System Events" to exists process "Mathematica"
if the result then quit application "Mathematica"
share|improve this answer
    
Nice and a specific answer to the question. Thx for the help. Also I very much appreciate the explanation for why my script didn't work. Thx. –  Jagra Jul 18 '12 at 12:56

The tl;dr Version

  1. Execute crontab -e in Terminal.
  2. Paste in 5 14 * * 1-5 osascript -e 'tell application "Mathematica" to quit' on a new line.
  3. Save.

The Detailed Version

I'm far from an Applescript expert, so I can't offer much feedback on why your script isn't working, but I can offer an alternate (and simpler) option.

Pretty much any UNIX-like system, including OS X has the cron daemon, which can run commands on a set schedule. OS X also has launchd, but that's a bit trickier to use, so we'll stick with cron.

The crontab format

Cron is just a plain text file that executes commands based on a simple structure.

  • Each line in the crontab is a separate job, in the form minute hour day-of-the-month month day-of-the-week command, with each field separated by a space.
    • * means any time, and you can put comma-separate multiple entries (1,3,5) or use a hyphen for a range (1-5).
    • Hours are on a 24-hour clock.
    • Note the distinction between day of the month and day of the week. Be careful setting both of these, you probably only want to use one or the other; i.e. if day of the month is 13 and day of the week is 5, your job will only run on Friday the 13th, not every Friday and every 13th of the month).
    • Days of the week go 0-7, with both 0 and 7 meaning Sunday.

Examples

0 15 * * 1-5 /usr/bin/blah would execute /usr/bin/blah at 3 PM every Monday to Friday on any calendar date of any month.

15 * 1 1-6 * /usr/bin/blah runs the same command at 15 minutes of every hour on the first day of January through June, regardless of what day of the week it is.

Your specific cron entry

Based on the schedule in your question, to quit Mathematica at 2:05 PM every weekday, the applicable line would be 5 14 * * 1-5 osascript -e 'tell application "Mathematica" to quit'

  • osascript runs Applescript from the command line, either from a file, or a one-liner you specify (as we're doing here).

Adding a cron job

  1. Open Terminal
    • Optional: if you're not comfortable with the vim editor, change the shell editor to one you're comfortable with (I suggest nano if you don't have a preference) by running the following command: export EDITOR=nano (change nano to your preferred editor). Note that you should probably stick to shell-based editors, GUI editors may not work with cron's peculiarities in saving the files.
  2. Type crontab -e and hit enter to open the crontab in the editor.
    • Each user has their own crontab, and the commands are executed with their permissions, so you should do this as whatever user is running Mathematica in the first place.
  3. On a new line (or the first line if there are no other entries), enter the command we determined earlier: 5 14 * * 1-5 osascript -e 'tell application "Mathematica" to quit'.
  4. Save the file (control-O in nano) and quit your editor.

That should do it. Obviously you'll want to test it out to be sure there aren't any formatting errors in your crontab, but that should be all you'll need to quit on a schedule.

share|improve this answer
    
Looks like a robust alternative. Very likely a better long term solution. I do want to get the Applesrcipt going to become more familiar with it if nothing else. Still you make a intriguing suggestion. Thx –  Jagra Jul 18 '12 at 12:34

Ok, well, here's how you would make launchd run at 2:05 p.m.

Copy this using a text editor and save it as ~/Library/LaunchAgents/com.tjluoma.quit-at-time.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.tjluoma.quit-at-time</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/bin/killMathematica.sh</string>
    </array>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>14</integer>
        <key>Minute</key>
        <integer>5</integer>
    </dict>
</dict>
</plist>

Note that you'll need to logout/login or use launchctl load ~/Library/LaunchAgents/com.tjluoma.quit-at-time.plist

Now, the /usr/local/bin/killMathematica.sh line could point to whatever it is you want to run: an app, an AppleScript, or a shell script.

The easiest solution is a shell script:

#!/bin/zsh

APP='Mathematica'

PID=$(ps cx | fgrep "$APP" | awk '{print $1}')

    # if $APP isn't running, then just exit already
[[ "$PID" == "" ]] && exit 0

    # if we get here, then $APP is running
    # tell it to quit using AppleScript
    # if this exits properly, the script will exit too
osascript -e "tell application \"$APP\" to quit" && exit 0

    # if we get here, AppleScript didn't work, so let's try
    # sending the app a 'kill' message
kill -QUIT "$PID" && exit 0

exit 1

# EOF

save that as '/usr/local/bin/killMathematica.sh' (or wherever) and make it chmod 755

Test it out and see if it works.

Now that I've given you the answer to the question you asked

Here's a better solution: use Keyboard Maestro. It's much easier.

How easy?

Boom. Done.

No launchd. No cron. No AppleScript.

(I had to use VLC instead of Mathematica because I don't have Mathematica installed.)

share|improve this answer
1  
You'll need to edit the launchd plist so it only executes on weekdays. I think this needs to be done via an array of dictionaries in the StartCalendarInterval key. –  robmathers Jul 18 '12 at 0:18
    
I appreciate the solution. But why not do the whole thing in AppleScript? Does it have some problem with reliability or robustness I should know about? –  Jagra Jul 18 '12 at 12:54

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.