Automatically symlink directory files into another directory

Anytime I add something to ~/config/, I would like it to be symlinked into ~. I don't want to run ln -s every time.


Solution 1:

There is iwatch utility, it can do this. In my Ubuntu I have installed iwatch package to get it. It may be used as a daemon or a command line tool.

I cannot guarantee the solutions below are foolproof and well written. Treat them as a proof of concept.

Command line

In my tests I did not manage to make the single instance of iwatch itself perform one action when a file is created but another action when it is removed. Maybe you can do this somehow. I decided to parse its output instead and then decide (case statement) what to do. Run this:

cd ~/config
iwatch -e create,moved_to,delete,moved_from -c "printf '%s %s\n' %e %f" ./ |
while read -r e f ; do
   f="$(basename "$f")"
   case $e in
      *IN_CREATE|*IN_MOVED_TO)
         ln -s "config/$f" "../$f"
      ;;
      *IN_DELETE|*IN_MOVED_FROM)
         test -L "../$f" && rm "../$f"
   esac
done

Play with touch, mkdir, mv, rm etc. in ~/config in another console to test the solution.

Notes:

  • Symlinks are created for new files/directories, they are removed when targets are removed; mv is also covered.
  • This works well for me even with somewhat troublesome names like foo bar or a"b.
  • During creation: if there is already an object in ~/ occupying the name then no symlink will be created to overwrite it – no data loss.
  • During removal: symlinks with proper names are removed no matter where they point to, non-symlinks are left intact thanks to test -L ….

Daemon

The iwatch daemon reads its config file, you can tell it what to do. Not only have I had a problem defining two different actions there but also escaping some characters inside the config file. The easiest solution was to call bash script to do the job.

Before you begin, note:

  • Paths should be absolute, modify them to fit your case.
  • Also USERNAME needs to be modified in the config file and in the script.

This is the snippet to paste into the config file (/etc/iwatch/iwatch.xml in my OS) just before </config>:

<watchlist>
    <title>symlinks</title>
    <contactpoint email="USERNAME@localhost" name="USERNAME"/>
    <path type="single" events="create,moved_to,delete,moved_from" alert="off" exec="/home/USERNAME/bin/link_config %e %f">/home/USERNAME/config</path>
</watchlist>

(You may want to remove default watchlist section, if any.)

Path to the script is /home/USERNAME/bin/link_config. This is the quick and dirty script:

#!/usr/bin/env bash

p="/home/USERNAME"
e="$1"
f="$(basename "$2")"

case "$e" in
    *IN_CREATE|*IN_MOVED_TO)
        ln -s "config/$f" "$p/$f"
        ;;
    *IN_DELETE|*IN_MOVED_FROM)
        [ -L "$p/$f" ] && rm "$p/$f"
esac

I think if you omit asterisks (*) and leave bare IN_CREATE etc. then the script will ignore directories created in /home/USERNAME/config; maybe this is what you like more. This applies also to command line version.

Make the script executable:

chmod u+x /home/USERNAME/bin/link_config

Set other permissions as you need. The daemon will run it as root anyway (at least in my OS it was so).

Start/restart/reload the daemon and have fun.