Run AppleScript everytime a file is opened.

How would I set things up so that every time I opened a file a particular AppleScript runs?
Could I have a folder so that each time any file in that folder was opened an AppleScript was run? (the same applescript for every file in that folder)

Or am I completely barking up the wrong tree?

Hello.

There are no “hooks” for doing this.

It would be very expensive as you would have to poll for the status of your file, like
checking the table returned by the lsofen command.

It would be better to use some hooks from the apps that opened the file, or open the file from applescript
via the apps in question. I have read that you can use GUI scripting to “hijack” what happens when you click on a menu item in an app. But that is as far as my knowledge go in that direction.

Best Regards

McUsr

launchd

Thanks. It sounds like the easiest plan would be to have an “open special file then do something in addition” script rather than call a “something in addition” script by opening a “special file”.

Hello.
@ mikerickson
I think you got it right it easier to deliberately open that special file in some special way scanning the opening
of all files, looking for the one special. That is: if Craig has a “how to” on how to track the opening of a file with launchd, which I hope he has, then it would be really easy to do some stuff on that file regardless of the application it was opened with.

@Craig: I thought lauchd only would track changes of a file if you use the launchd path, but I’m not certain if an opening of a file, would generate a “changed event” to its containing folder. Or is there some other functionality of launchd which has escaped me?

If you now how to use launchd to track opening of files I’d really love to hear about it!

Best Regards

McUsr

Am relatively new to applescript, so please take that on board.

This may offer something or equally could be pure balderdash.

Could this be done with a stay open script ?

though as says McUsr

Generally, though not always, a file’s window contains its name or a part of it .

Would something on the following lines suffice or at least be a beginning of a solution ?

Name of file: “No Subject Saturday, 19 June, 2010”

Location of file: “/Users/mikerickson/Documents/No Subject Saturday, 19 June, 2010”

Application that opens file (say) “Microsoft Word”

Save the following as a stay open application and run it.

on idle
	set theFile to "/Users/mikerickson/Documents/No Subject Saturday, 19 June, 2010"
	set nameoffile to name of (info for theFile)
	if (get running of application "Microsoft Word") is true then
		if (get name of every window of application "Microsoft Word") contains  nameoffile then
			activate me
			display dialog "Do whatever you like from now on"
		end if
	end if
	return 0.5
end idle

If this works replace dialog with your preferred script. ???

Val

Something could maybe be done with Hazel.

Set the condition to “IF date last opened is in the last 3 minutes”
Set the action to “run applescript”, and point her to the script.
There will be a delay of 0-3 minutes before the script runs (because that’s how often Hazel runs).

This isn’t exactly the same as “run script when file is opened”, but the end result is the same.
You need to say ‘in the last 3 minutes’ so she doesn’t miss any files that were opened after her last pass.
Hazel will not rerun the same rule on the same file when it matches the same condition a second time, so I don’t think there’s a risk of running your script twice on the same file.

The 3 minutes limit is not absolute; when she misses files it would need to be a bit longer.

If I read correctly, the OP was not wanting to poll the name of a document but every files embedded in a given folder.
This requires that the idle routine grabs the path of the opened document and check if it starts with the path of the polled folder.
With Pages or Numbers, this kind of code would be OK :


--{code}
property theApp : "Pages"
property folder_to_poll : "/Users/yvan_koenig/Desktop/"
--=====

on run
	tell application "System Events"
		tell process theApp
			set count_of_windows to count of windows
			repeat with i from 1 to count_of_windows
				my poll_doc(name of window i)
			end repeat
		end tell
	end tell
end run

--=====

on poll_doc(doc_name)
	tell application theApp
		try
			set maybe to get path of document doc_name
			if maybe starts with folder_to_poll then
				-- do your duty
			end if
		end try
	end tell
end poll_doc

--=====
--{code}

Yvan KOENIG (VALLAURIS, France) dimanche 20 juin 2010 21:29:29

Hello.

What all of you guys delpucci, alastor and Yvan came up with is brilliant.

I have an addition to delpucci and alastor’s solutions:

I believe you also could implement similar functionality by calling spotlight thru mdfind, and I know you could do it with unix find, and that wouldn’t be so bad when you already know the path. Mdfind would be quite fast as well.

And I think you can implement Yvan’s solution with almost any app if you buy “something” from prefab and hijacks the “open” command of the app.

But we really don’t know if it is about a file opened from an app.

Best Regards from beautiful Southern Norway as it only can be on day like this. :slight_smile:

McUsr

The caveats in my earlier post apply.

McUsr is correct re fast.

Put the “activating” files in (say) My Special Folder in your Documents folder.

Then save script as stay open to be activated say at login.

Obviously needs improvement but is getting there.

on idle
	set parentfolder to path to documents folder
	set myfolder to parentfolder & "My Special Folder" & ":" as string
	set ppath to quoted form of POSIX path of myfolder
	if (do shell script "mdfind -onlyin " & ppath & " 'kMDItemLastUsedDate  > $time.now(-0.5)' ") is not "" then
		activate me
		display dialog "Do whatever you like from now on"
	end if
	return 0.5
end idle

Val

The OP (me) was looking to run a script when a particular file was opened. The file idea was a workaround because I know that “folder actions” exist but am unaware of “file actions”.

Many fine ideas (that I don’t quite understand yet) came out of this.

Hello.

The idea to some of them is to poll for the event if you know which application you are going to open it with.
The other possible way to practically poll for the event is to utilize the “last accessed” system attribute, like in Hazel, or implement the functionality via find or mdfind.

Best Regards
McUsr

Mmmm.
What we’re looking for is essentially a way to attach an “open with” action to an “open” event.
Mik said ‘file actions’.
Right?

Is there a way to observe an ‘open’ event? fseventer? launchd? (of which I really know nothing)
How do folder actions do it?
Can we use that for other purposes? Oooh, the possibilities!

Yes, good idea.
It would be much quicker than waiting for Hazel to do her rounds.
The full solution would be the same, I think.

Yes, using ‘last opened’ won’t tell you which app did the opening.
That’s why you’d need an “open with” action.
But ‘observing’ the event might tell you which app did the opening, and the rest is coding.

If it just a few specific files a stay-open script would do it.
When it’s many, or if they change often, the ‘open with’ action seems a better solution - if it can be created.
But a folder action can’t do that; it does not respond to a file being opened in the watched folder.

Yet another posssibility:
Create a service (in Automator) to open these files from the context menu.
The embedded script would perform the required actions, and open the file (and do these things in any order you want).

And, finally:
UiActions, but that’s $$.
With this, you attach scripts to actions within an app, so I assume such a script could also do things to the app’s files.

There’s a command line program called fs_usage that could help. It monitors all file system usage and prints out the results. It requires root privileges. It prints out a lot of information so we need to filter that info.

Here’s an example usage. Suppose I wanted to know when a particular file is opened, say a file on my desktop called “test.txt”. I can filter on an application name and a file path to eliminate almost all of the output from fs_usage. The application you filter on must be running, and thus I’ll choose to filter on the Finder because it’s always running.

So here’s how I would run the command:

sudo fs_usage -f filesys Finder | grep /Users/hmcshane/Desktop/test.txt

So try running that from the Terminal, and then launching your file. You’ll see you get output when you do. Now you just have to figure out how to use it from applescript.

mikerickson wrote

In order to confine the “activating” to a particular file I have come up with the following variation of post 10 on this thread.

(Using Adam Bell’s scripts from http://macscripter.net/viewtopic.php?id=24765 and Nigel Garvey’s from http://macscripter.net/viewtopic.php?id=33218)

I really have no idea whether kMDItemLastUsedDate is a reliable indicator for “Last Opened”. It appears to be, but I really don’t know.

Name of file: “No Subject Saturday, 19 June, 2010”

Location of file: “/Users/mikerickson/Documents/My Special Folder/No Subject Saturday, 19 June, 2010”

Substitute your applescript for the dialog.

on idle
	set parentfolder to path to documents folder
	set myfile to parentfolder & "My Special Folder" & ":" & "No Subject Saturday, 19 June, 2010" as string
	set tPath to quoted form of (POSIX path of myfile)
	set a to do shell script "mdls -name kMDItemLastUsedDate " & tPath
	set b to text ((offset of "=" in a) + 2) thru end in a
	tell (current date)
		set its day to 1 -- Overflow precaution.
		set its year to text 1 thru 4 of b -- Automatic .
		set its month to text 6 thru 7 of b -- . text to integer .
		set its day to text 9 thru 10 of b
		set its hours to text 12 thru 13 of b --  . coercions.
		set its minutes to text 15 thru 16 of b
		set its seconds to text 18 thru 19 of b
		set theDate to its date (date string) -- A date string in the user's preferred format.
	end tell
	if theDate > (current date) - 1 then
		activate me
		display dialog "Do whatever you like from now on"
	end if
	return 0.2
end idle

Save and run as a stay-open application.

So far, each time that I have opened the file the dialog appears. This could give some grounds for believing that your applescript could be triggered each time the file is opened.

Val

Consider, that running a script including a spotlight call 5 times a second is pretty expensive in consuming resources

Hello.
@Val
Could you please put into code-exchange?

@Stefan
It’s kind of interesting, because no one of us but the OP knows what kind of file we are talking about.
If it is a small system file, then 5 times per second might be too slow!
On the other hand if there is a document type of file, then He could estimate the file to be open at least say 1 minute. then you could do the call every 50th second and be pretty sure the process will able to track when the file is open. 250 times cheaper than previous solution, but still nothing for a machine on batteries.

@AllOfYou
-Living in the belief that tripwire is able to track open files: (That may be wrong.)
We can install tripwire, so why shouldn’t one be able to do the same via a homemade kext (kernel extension).
-Thinking loudly, I think that the tripwire source, may provide a good solution should no other suffice.

Best Regards

McUsr

In any event I have just learnt that kMDItemLastUsedDate apparently is only updated when the file is opened by being double-clicked.

So my above contributions (both) have little use. Sorry about that.

Val

Hello.

@Val the code is still very interesting with its limitations.

Then we must go one level below into the i-node tables. And use unix-find or whatever.
They should be updated pretty timely.

Sorry to say but find doesn’t provide finer granularity than 1 second.
Most code stolen shamlessy from delpucci :cool:

on idle
	set parentfolder to path to documents folder
	set myfolder to parentfolder & "My Special Folder" & ":" as text 
	set ppath to quoted form of POSIX path of myfolder
	if (do shell script "( find  " & ppath & " -atime 1s  -name " & (quoted form of  "Saturday 19th of june 21Am ") & " | echo $? ) ") is 0 then
		activate me
		display dialog "Do whatever you like from now on"
	end if
	return 1
end idle

I’m not sure that it runs as it stands. Not tested.

Best Regards from beautiful Southern Norway on a Sunny Afternoon.

McUsr

Since the question has been raised, the file would be an Excel workbook. The script would 1) assign buttons on the sheets to various scripts and/or 2) start a background script that mimics VBA’s Worksheet_Change event.

Its not a particular project that I have in mind, but a technique that could be applied to any workbook.

If I’m willing to have an “infinite” loop mimic the Change event, I should(?) be willing to use the resources for a “is it open yet” script.

On the other hand, opening the file via script rather than the Finder is a good option when I am the sole user. For other users, the background looping might be easiest. (Or…If I can cobble together the equivalent of a BeforeClose event, I could close the wb in such a way as to display an “Open Me With AppleScript” sheet to remind the users if they open it with Finder.)