React when file is renamed via Folder Action

I tried to search for a solution but I only get about a bajillion “how do I rename a file” posts…

I need to write a Folder Action that reacts when a file is renamed and I’m having a hard time with it.

For one, i can’t seem to find any mention of this as an event. I found an article on MacTech that says “A list of available Folder Action handlers can be found in the Folder Actions suite in the Standard Additions scripting addition that is installed with Mac OS X.”

Sure enough, there’s a screenshot of the Catalog window in AppleScript Editor that lists a bunch of events for which handlers can be created but I can’t find the “Standard Additions” catalog anywhere. (Standard Addition.osax)

I could whip this out in VB in a heartbeat, but being a Mac, I’m kinda stuck!

Here’s the basics:

  1. a file is created in a folder
  2. the file is written (this could take some time)
  3. the file is re-named

I need to capture event #3. Maybe there’s a better way than a Folder Action?

-Brian

According to the AppleScript Language Guide possible the possible folder actions are:

adding folder items to - A script handler that is invoked after items are added to its associated folder.
closing folder window for - A script handler that is invoked after a folder’s associated window is closed.
moving folder window for - A script handler that is invoked after a folder’s associated window is moved or resized. Not currently available.
opening folder - A script handler that is invoked when its associated folder is opened in a window.
removing folder items from - A script handler that is invoked after items have been removed from its associated folder.

[del]There is no folder action when a filename is changed.[/del] edit: See Nigels Post that I was wrong that renaming a file will trigger both adding folder items and removing folder items.

On the other hand there is launchd (an collection of initd, cron and other unix utilities) that can launch a job when an file has changed using watchpaths. If you set an directory as it’s watchpaths the command/job is launched everytime the folder has changed including editing file names.

Hello.

I’d go for launcd and watchpath to the parent directory, I mean the directory the files resides in, that launchAgent, would then be triggered whenever a directory entry is change (file creation, deletion, and renaming=-.

I think you can use the comm utility to track which event happen, by feeding it the previous file listing, and the new one. you will want to have both the inode number, and the filename in that listing.

I also think that the launchAgent may be triggered when there are just attribute changes.

You will have to investigate all of it yourself, as I have no time to verify this today. the man command would give you access to the help for the various commands in a Terminal window, and give you further pointers. For launchd i recommend googling whatever question you have.
But there is also an article in the unscripted section here that is well worth to read.

Hello, thank you for your good suggestions…

Apparently a file rename qualifies as an “adding folder items to”.

I was able to capture the event and handle the files by using this:


on adding folder items to thisFolder after receiving addedItems
	repeat with anItem in addedItems
		... my code here
	end repeat
end adding folder items to

I’ve had folder actions just stop working on occasion so I’ll probably look into the launchd job after things settle down a bit.

Thanks again!

-Brian

No explicit action. But because of the way folders are watched, changing the name of an item triggers both ‘adding folder items to’ and ‘removing folder items from’ folder actions. This often causes confusion when people write folder actions to rename items added to folders ” because the renaming of the items retriggers the actions.

As long as the process which renames Brian’s file isn’t triggered by ‘adding folder items to’, the files can be safely renamed and the renaming detected with ‘removing folder items from’. The ‘after losing’ parameter will receive aliases (or possibly bookmarks) to the renamed items and it’s an easy matter to detect whether they’re still in the folder (just been renamed) or not (moved after renaming).

launchd is probably a more reliable system though.

Hello Nigel.

I thought an alias, and a bookmark was the same thing, could you please tell me the difference? I know about the difference with regards to security for bookmarks, when an app is sandboxed, but are there other differences than that? I think I looked around, because I couldn’t figure it out, but I concluded with, maybe errantly that it is the same thing when there aren’t any security/sandboxing involved. Please do enlighten me, if I’m wrong, I am not overly confident in differing between the two. :slight_smile:

Edit

I had to google it, and there seem to be size difference, as the Finder, stores a resource fork twice, whereas a bookmark, stores the same resource fork once.

Here is a full treatment of the subject.

I certainly hope so…

The change rate on this folder comes in bursts with 16 files following processes 1-3 above. I’m getting continuous “(com.apple.FolderActions.folders) Throttling respawn: Will start in X seconds” messages in Console. It’s leaving about 1/3 of the files behind each time a batch comes in.

I’m guessing this is because it’s being asked to respawn before it’s finished with the previous run? (When I do this in a windows environment I just spawn the handler on a separate thread… it appears that folder actions are single-thread?)

FYI the operation that is renaming the files to begin with is not reacting to any kind of folder action. It begins by writing a .TMP file and when it’s finished, the file is renamed. This prevents downstream steps in the workflow from attempting to process the .TMP files. I simply filter for the proper extension.

Any tips for resources on writing launchd jobs? (How many scripting languages am I going to have to work in today?!)

-Brian

Thanks Nigel! I wasn’t aware of that, I always thought nothing happens. I’ve added your comment to my post.

Nevermind. I found a really cool little app called “Lingon” that makes launchd job scheduling a breeze. I just tweaked the AppleScript to search the directory (rather than accept incoming changes) and set the launchd job to run every 10 seconds.

It’s working like a champ!

Thanks for all your help!

-Brian

Hello.

Lingon is a cool little app, but if one can’t afford it, then one can copy one the LaunchAgents that are already there, and use that as a boilerplate template, you’ll have to change what must be changed, like the job name, parameters and so on however, but it is a better point to start than from scratch. TextWrangler is good at editing plist files, and free.

I don’t know if Lingon starts and stops the job for you nowadays, but earlier on, you’d have to load the property list, preferably with a -w switch if you wanted it to persist between boots, then start the job (without the plist extension),
then stop it, and unload the property list again, with the -w if one wanted the change to persist between reboots.

All this is described in the man page for launchctl.

I find folder actions unusable, as long as they aren’t reliable.

The version of Lingon I use on my servers is necessarily v2. (Snow Leopard Server on XServe hardware)

v2 handles the loading/unloading of the launchd job for you but you must log out (or restart) afterward. This isn’t a Lingon limitation but an OS requirement.

I’m a “get it done” kind of guy and if I had a couple of days to play with plist files I might figure out how to do it myself, but project completion takes precedence in this case!

-Brian

Hello.

Last time I looked into this, then I could load, unload, start and stop from the command line, by using launchctl, reading the manual page, start and stop seems to be discouraged, but you can stop a job with the unload command.

If you don’t mind logging out and in again, then fine, but I don’t think you really have to.

And it is fine if Lingon solves it for you. I have the same version, but I think you have to pay for the newer ones, but you’l still have to read a tiny bit, maybe not if you use watchpaths.

The most important thing is always getting the job done, and the solution always matters more than the problem, (as long as the quality of the solution is good enough) :slight_smile:

I am sure that the app store version of LIngon is worth it’s price, because configuring launchd.plist, are one of the more complex and intricate areas of OS X.

I use the cron interface, whenever I can. :slight_smile: (Timed jobs)

I’m afraid I don’t really know anything about bookmarks, except that they’re items of «class bmrk», introduced with Snow Leopard, which the system now passes to droplets instead of the aliases («class alis») the dictionary tells you to expect. I suppose I should have looked to see what’s passed to folder actions while I had them set up to check my previous post. :rolleyes:

That seems to be about alias files rather than the references passed to scripts.

The two are closely related. AppleScript aliases were so-named because they use the same Carbon code as was used to resolve alias files since the pre-X days. You can almost consider them as cut-down old-style alias files stored in an AS objects. But alias files since Snow Leopard use different code, and are technically bookmarks. They are resolved with newer Cocoa code (which falls back to the old code if needed), and they too can be stored in cut-down form in resolvable AS objects.

I don’t know how the change happened is AppleScript. I wonder if something in the OS was changed to automatically re-route some calls to the older Carbon APIs to use the new Carbon APIs.

Hello.

Reading Shane’s post, I gather that AppleScript regards the two as synonyms, and works the same way, but maybe the classname differs, and that’s that, with regards to how they are passed around to droplets for instance.

You should be able to get the original item at least after you have cast the bkmrk file to an alias file. (Haven’t tested it yet.)

There is an difference between alias file and alias record (known as alias class in AppleScript). Alias records are deprecated to the Carbon Core Deprecations document and Apple suggest no longer to use alias records. I think that’s the reason why your get bookmarks on a droplet, the system is up-to-date. Either way, if an alias record and bookmark are the same or not, alias records should no longer be used.

If an alias record or bookmark is stored on disk then both will turn into an alias file (Apple’s implementation of symbolic link). So there is/should be no difference between bookmarks and alias records once they’re stored on the disk according to Apple’s documentation. But Shane’s tests proves on cocoabuilder otherwise, files created from the Finder and from the NSURL class using the writeBookmarkData method are different in size.

In addition to shane’s post on cocoabuilder: If you read the Alias Manager Reference Apple is talking about two types of alias records, standard and minimal alias records. So I wouldn’t be surprised that there are two types of alias files too (standard and minimal). Maybe, this is guessing, NSURL uses the minimal alias file type to store them and the Finder is using the standard alias file type. Like I said it’s a guess but maybe it clarifies the difference in file size.

As I understand it, bookmarks are a replacement for alias records – all alias files made on Macs running 10.6 or later are bookmarks. They are different, although they might be largely a matter of different packaging.

Hello.

Thanks for the link DJ Bazzie Wazzie, I think I have come across the document earlier, but neglected to read it, due to the red crayons in it. (Every method mentioned was deprecated at least in 10.8)

I have also noticed beforehand, that I can read an alias as a bookmark with CoreServices, I just haven’t tried it the other way around.

There are also two types of Bookmarks, the one regular type, and the other with security options, to be used with Sandboxed Applications. (I guess this type is nothing you would/could use with finder or AppleScript for security reasons anyway, so this bookmark type fits nicely outside the picture, leaving us with the two alias types.

Thanks for the CarbonCoreDeprecation document as well! it has also slipped by me.

Edit

I really recommend using the /usr/include/AssertMacros.h if someone is programming in C! They are utterly cool! :slight_smile: There are usage examples all over in older XCode Carbon Projects, but they are very handy when using CoreFoundation as well, (and really easy to use).

For the record: Finder 10.5 (Leopard) already created an hybrid that are bookmark compatible but also alias records. Finder in Tiger was an Carbon application so I don’t think it was using bookmark compatible alias files. Also the hexdump of an alias file looks a lot different in Tiger than in (Snow) Leopard.

Now it makes all sense knowing that 10.5 is already using different kind of alias files that were compatible with book marks and alias records. Because what you’ve said is that when I create an alias in Tiger I can still use it in Leopard and Snow Leopard. When I create an alias in Leopard I can use it in Tiger and Snow Leopard. So far so good but when I create an alias in Snow Leopard, I can use it in Leopard but it won’t work in Tiger. So it’s all upwards compatible but not entirely downwards compatible. Conclusion is that Finder is creating different alias file through Tiger (Alias Record), Leopard (hybrid) and Snow Leopard (Bookmark).

Interesting :slight_smile:

And hardly anyone noticed. If only all changes were handled so gracefully…