9 Replies Latest reply: Jan 28, 2010 12:04 PM by red_menace
BenChase Level 1 Level 1 (10 points)
Maybe there is a better way to do this but I want an action to take place whenever the modification date of a certain file changes. This is what I use but it errors because of a timeout after about 10 mins if the mod date doesn't change. I need this to be checking all the time.

on idle
tell application "Finder"
set UpdatedOLD to modification date of file aFile
set UpdatedNEW to modification date of file aFile
end tell

repeat while UpdatedOLD = UpdatedNEW
tell application "Finder"
set UpdatedNEW to modification date of file aFile
end tell
end repeat
end idle

How can I get this to work?
  • red_menace Level 6 Level 6 (14,760 points)
    The idle handler is run after the run handler completes, repeating after the returned time interval (I am guessing that you are saving the script as run-only). Your example is timing out in the repeat statement because you aren't doing anything to terminate it (UpdateOLD will always equal UpdateNEW(. A better approach would be something like:

    <pre style="
    font-family: Monaco, 'Courier New', Courier, monospace;
    font-size: 10px;
    font-weight: normal;
    margin: 0px;
    padding: 5px;
    border: 1px solid #000000;
    width: 720px;
    color: #000000;
    background-color: #DAFFB6;
    overflow: auto;"
    title="this text can be pasted into the Script Editor">
    tell application "Finder" to set UpdatedOLD to modification date of file aFile -- get the original date

    on idle -- check
    tell application "Finder" to set UpdatedNEW to modification date of file aFile

    if UpdatedOLD is not equal to UpdatedNEW then -- the date changed
    -- do stuff whenever the modification date is changed
    tell application "Finder" to set UpdatedOLD to modification date of file aFile -- update
    end if
    return 30 -- check again in 30 seconds
    end idle
    </pre>
  • Hiroto Level 5 Level 5 (5,600 points)
    Hello

    I think you have stepped on a land mine set by OSX 10.6.
    There's a fatal bug in Apple Event Manager in 10.6 such that one event in every 65535 events will be lost and never be replied, which will result in Apple Event timeout error on sender. This bug has been reported shortly after the 10.6 release and has not yet been fixed as of 10.6.2.

    In your current script, you're continuously sending event to Finder and sooner on later send an event with the specific event id that is doomed to be lost. Judging from the time till you see the time out error, that is 10 min, you're at most sending 65535 / 600 = 109.225 events / sec to Finder. You can reduce the number of events by inserting some delay, e.g. 'delay 1' in your repeat loop but it can only defer the failure.

    cf.
    Re: Timed Out (Silence)
    http://lists.apple.com/archives/applescript-users/2009/Oct/msg00117.html

    Re: spurious timeout on nth Apple event on Snow Leopard
    http://lists.apple.com/archives/applescript-users/2009/Nov/msg00041.html

    ---
    A better way to achieve your task would be to let a launchd agent watch the file.
    A recipe is as follows.

    1) Save a compiled script in :

    ~/Library/Scripts/launchd/watchdog.1.scpt

    with contents :

    --SCRIPT
    -- Here put your script that is to be triggered when the file is modified.
    -- e.g.
    tell application "System Events"
    display dialog "The file is modified." giving up after 10
    end tell
    --END OF SCRIPT


    2) Save a UTF-8 plain text file in :

    ~/Library/LaunchAgents/watchdog.1.plist

    with contents :

    <?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>watchdog.1</string>
    <key>Disabled</key>
    <false/>
    <key>Program</key>
    <string>/usr/bin/osascript</string>
    <key>ProgramArguments</key>
    <array>
    <string>osascript</string>
    <string>/Users/USER_NAME/Library/Scripts/launchd/watchdog.1.scpt</string>
    </array>
    <key>WatchPaths</key>
    <array>
    <string>POSIX_PATH_TO_THE_FIILE</string>
    </array>
    </dict>
    </plist>

    *Change USER_NAME to your user name and POSIX_PATH_TO_THE_FIILE to the POSIX path to the file to be watched.



    3) Issue the following command in Terminal to load the launchd agent :

    launchctl load ~/Library/LaunchAgents/watchdog.1.plist

    Or

    3a) Log-out and re-log-in to load the launchd agent.

    *The name 'watchdog.1.plist' and 'watchdog.1.scpt' and the script's location '~/Library/Scripts/launchd/' are mere examples. You may change them as you see fit.



    cf.
    http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/m an5/launchd.plist.5.html
    http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/m an1/launchctl.1.html

    Good luck,
    H

    Message was edited by: Hiroto (fixed typo)
  • red_menace Level 6 Level 6 (14,760 points)
    I forgot about that one. I haven't run into it yet, though - is that total events, or events per application?

    Using the idle handler as intended, checking say, every 15 seconds or so only delays the inevitable to a couple of hundred hours.
  • Hiroto Level 5 Level 5 (5,600 points)
    Hello

    I'm not sure but I'd think the AEM's event id is assigned globally, not per application or even per login session (under 10.6), and only reset by reboot.

    So those who restart the machine often enough won't see this bug while those who use scripts sending events intensively will step on it soon, I suspect.

    Well, they need to fix this asap, I'm sure!

    Hiroto
  • red_menace Level 6 Level 6 (14,760 points)
    Found it (there was a break in the thread and I didn't go back far enough) - looks like it is global. Well, at least they know what it is...

    http://lists.apple.com/archives/AppleScript-Users//2009/Oct/msg00085.html
  • BenChase Level 1 Level 1 (10 points)
    Thanks for the advice. If this is a problem with just 10.6 wouldn't one solution be to have it run on 10.5? I am doing all the testing on my computer with 10.6 but eventually I am going to be moving it to another computer that is currently running 10.5. I was thinking about upgrading but maybe now I'll wait, as long as 10.5 doesn't also have the problem.
  • red_menace Level 6 Level 6 (14,760 points)
    It would really depend on what you wind up doing. The Launchd method wouldn't have the problem, and using the *idle handler* in a stay-open application that checks every 10-15 seconds wouldn't have the problem either if you restart daily (or even once a week/month or so).

    Using either of the above methods would also be better than the script constantly running in a repeat loop, since the script would only run periodically instead of constantly.

    I'm not sure if the script you originally posted was complete, but there will be variable scoping issues if that is the case. An example of a complete working (save as a stay-open application) is:
    <pre style="
    font-family: Monaco, 'Courier New', Courier, monospace;
    font-size: 10px;
    font-weight: normal;
    margin: 0px;
    padding: 5px;
    border: 1px solid #000000;
    width: 720px; height: 340px;
    color: #000000;
    background-color: #DAFFB6;
    overflow: auto;"
    title="this text can be pasted into the Script Editor">
    -- watch a file's modification date

    property someFile : missing value -- the file to watch
    property interval : 15 -- time duration between checks
    global previousDate -- the previous modification date


    on run -- set things up
    if someFile is missing value then set someFile to (choose file) as text
    tell application "Finder" to set previousDate to modification date of file someFile -- get the original date
    end run


    on reopen -- application double-clicked while runnning
    -- in this case, just move focus to the application - can also do something like:

    -- set someFile to (choose file) as text -- reset the watched file
    -- tell application "Finder" to set previousDate to modification date of file someFile -- get the original date

    end reopen


    on idle -- check
    tell application "Finder" to set currentDate to modification date of file someFile
    if currentDate is not equal to previousDate then -- the date changed
    doSomething()
    tell application "Finder" to set previousDate to modification date of file someFile -- update
    end if
    return interval -- check again later
    end idle


    to doSomething() -- do stuff whenever the modification date is changed
    tell application "System Events" to set frontApp to (name of processes whose frontmost is true) as text

    activate me
    beep
    display alert "File changed" message "the modification date of file " & quoted form of someFile & " has changed." giving up after 5 -- the 'giving up after' time can be a bit fussy

    tell application frontApp to activate
    end doSomething
    </pre>
  • BenChase Level 1 Level 1 (10 points)
    Thanks for the replies. My script works it just times out when its in the repeat loop if nothing happens. I only showed you part of the script maybe a better explanation would help.

    The file is on a sever volume and is occasionally modified by other people. While in the repeat loop it is constantly checking for the mod date to be different then it was at the beginning of the "on idle". Once the file is modified the mod date changes and the repeat loop is exited and the rest of the process is completed. After the process is completed it "idles" back to the beginning and back into the repeat loop waiting for the file to be changed again.

    All of this work beautifully and is extremely simple except, like I pointed out, after about 10 minutes the script errors because of a timeout in the repeat loop. The file being checked is randomly modified from maybe a couple times a day to possibly every couple days. But as soon as its changed I need the rest of the process to be run as immediately as possible. Adding a delay, like previously mentioned, will just delay the inevitable if no modification is made within that time frame. And at a certain point adding too much of a delay would defeat the purpose anyway.
  • red_menace Level 6 Level 6 (14,760 points)
    The idle handler is normally called every 30 seconds (the default time can be changed), so using it as intended won't send quite the number of events that a repeat loop will. I'm not sure what the default response time using Launchd would be, but if you are needing a script to sit in a loop constantly getting the modification time, for now I think you are limited to using Launchd or Leopard. Hopefully this gets fixed in the next *Snow Leopard* update.