How to make Smart folder like physical folder with the combined contents of two folders

I have an application which looks for its data (*.txt) files in a folder. I have two folders containing text data that I need to keep separate, but it would be useful to have the application see the text data files as residing in one folder (containing the two sets of files).

I can create a script that maintains symbolic links to the files in the two folders in a third combined folder.

Rather than reinventing the wheel, I'd like to use a proven solution if possible.

The solution would have to deal with the case of file name clashes in a sensible way.


You could use launchd.

launchd lets you manage daemons and agents according to certain conditions.

What are daemons and agents?

From man launchd:

A "daemon" is, by definition, a system-wide service of which there is one instance for all clients. An "agent" is a service that runs on a per-user basis. Daemons should not attempt to display UI or interact directly with a user's login session. Any and all work that involves interacting with a user should be done through agents.

From http://developer.apple.com/library/mac/#technotes/tn2083/_index.html:

Daemons and agents, collectively known as background programs, are programs that operate without any graphical user interface. As a developer, you can use background programs to perform actions without user interaction, and also to manage a shared state between multiple other programs.

The difference between an agent and a daemon is that an agent can display GUI if it wants to, while a daemon can't. The difference between an agent and a regular application is that an agent typically displays no GUI (or a very limited GUI).

The daemon/agent is described in an XML file with extension plist. One of the conditions that can be monitored is changes in a folder. This will come in handy.

OK, let's get our hands dirty:

Let's say these are the 2 folders where you and your friend keep your files:

/tmp/folderstuart
/tmp/folderstuartsfriend

and this is the common folder for the application:

/tmp/folder

We want to monitor the two paths above and synchronize their contents with /tmp/folder.

This is the plist that does what we need:

<?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>notesfoldersync</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/bin/rsync</string>
        <string>-aE</string>
        <string>--delete</string>
        <string>--exclude='.*'</string>
        <string>/tmp/folderstuart/</string>
        <string>/tmp/folderstuartsfriend/</string>
        <string>/tmp/folder</string>
    </array>
    <key>WatchPaths</key>
    <array>
        <string>/tmp/folderstuart</string>
        <string>/tmp/folderstuartsfriend</string>
    </array>
    <key>ThrottleInterval</key>
        <integer>10</integer>
</dict>
</plist>

The plist monitors the two folders with key WatchPaths (see http://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html#//apple_ref/doc/uid/10000172i-SW7-SW8).

I suggest the program rsync for synchronizing folders. Option --delete ensures that files deleted in the monitored folders are also deleted in /tmp/folder. Other options are -aE to copy standard and extended HFS+ attributes, and --exclude='.*' to skip .localized, .DS_Store and other hidden files.

I added ThrottleInterval in case you want to set the minimum interval a job can be spawned. Default value is 10 s, that is, jobs will not be spawned more than once every 10 seconds.

Save the plist (see man launchd for a list of possible paths) as:

 /System/Library/LaunchDaemons/notesfoldersync.plist

Create:

/tmp/folderstuart
/tmp/folderstuartsfriend
/tmp/folder

and load (that is, enable) the plist:

sudo launchctl load /System/Library/LaunchDaemons/notesfoldersync.plist

Now create a file in /tmp/folderstuart:

touch /tmp/folderstuart/file.txt

and watch the magic happen: file.txt will be created within seconds in /tmp/folder. Delete it and it will disappear from /tmp/folder. It will also synchronize files created or deleted in /tmp/folderstuartsfriend.

Notice that this solution doesn't handle name collisions! If you can't ensure that files will be named differently substitute rsync with a script that rsyncs and does file name checking to avoid data loss.

If copying files is not an option, substitute rsync with a script that creates hardlinks (if both files are in the same filesystem I'd recommend hardlinks instead of symlinks). Before changing the plist unload it:

sudo launchctl unload /System/Library/LaunchDaemons/notesfoldersync.plist

When you're done, load it again.


Why not use a keyword or Spotlight Comment to tag the files in each folder, and then a Smart Folder to show them together?