Fellow Monks,

I've just made the first public release of a tool I've been working on called relink (Update: it's GPLv3), and I wanted to tell you about it in the form of a short narrative, below. But first, just to whet your appetite:

# Rewrite the targets of symlinks with Perl $ relink rewrite 's/foo/bar/' PATH(s) # Convert between absolute and relative targets $ relink abs2rel PATH(s) $ relink rel2abs PATH(s) # List links [only certain symlinks] $ relink list [-t '/foo/'] PATH(s)

In this fictional scenario, let's say I've been keeping activity reports. I decided to keep weekly logs, but since I was supposed to file them monthly, and I wasn't very careful about which weeks I began and ended the reports on, I've ended up with this mishmash of files and symlinks:

~/Documents$ find . ! -type d -printf '%y %p\t%l\n' f ./Reports Oct 2016/Report Weeks 40-42.xls f ./Reports Oct 2016/Report Weeks 43-46.xls l ./Reports Nov 2016/Report Weeks 43-46.xls /home/foobar/Documents/ +Reports Oct 2016/Report Weeks 43-46.xls f ./Reports Nov 2016/Report Weeks 47-51.xls l ./Reports Dec 2016/Report Weeks 47-51.xls /home/foobar/Documents/ +Reports Nov 2016/Report Weeks 47-51.xls

Later, I realize that a different directory structure would make much more sense. No problem, I'll just do that by hand:

~/Documents$ mkdir 'Reports 2016' ~/Documents$ for M in Oct Nov Dec; do \ mv -v "Reports $M 2016" "Reports 2016/$M"; done ‘Reports Oct 2016’ -> ‘Reports 2016/Oct’ ‘Reports Nov 2016’ -> ‘Reports 2016/Nov’ ‘Reports Dec 2016’ -> ‘Reports 2016/Dec’ ~/Documents$ find . ! -type d -printf '%y %p\t%l\n' f ./Reports 2016/Oct/Report Weeks 40-42.xls f ./Reports 2016/Oct/Report Weeks 43-46.xls l ./Reports 2016/Nov/Report Weeks 43-46.xls /home/foobar/Documents/ +Reports Oct 2016/Report Weeks 43-46.xls f ./Reports 2016/Nov/Report Weeks 47-51.xls l ./Reports 2016/Dec/Report Weeks 47-51.xls /home/foobar/Documents/ +Reports Nov 2016/Report Weeks 47-51.xls

D'oh! Forgot about those symlinks, now they're broken! But no problem, relink to the rescue!

~/Documents$ relink rewrite 's{/Reports (\w+) 2016/}{/Reports 2016/$1/ +}' ~/Documents$ find . ! -type d -printf '%y %p\t%l\n' f ./Reports 2016/Oct/Report Weeks 40-42.xls f ./Reports 2016/Oct/Report Weeks 43-46.xls l ./Reports 2016/Nov/Report Weeks 43-46.xls /home/foobar/Documents/ +Reports 2016/Oct/Report Weeks 43-46.xls f ./Reports 2016/Nov/Report Weeks 47-51.xls l ./Reports 2016/Dec/Report Weeks 47-51.xls /home/foobar/Documents/ +Reports 2016/Nov/Report Weeks 47-51.xls

Fixed! But it'd be much nicer if those were just relative symlinks... no problem!

~/Documents$ relink abs2rel ~/Documents$ find . ! -type d -printf '%y %p\t%l\n' f ./Reports 2016/Oct/Report Weeks 40-42.xls f ./Reports 2016/Oct/Report Weeks 43-46.xls l ./Reports 2016/Nov/Report Weeks 43-46.xls ../Oct/Report Weeks 43- +46.xls f ./Reports 2016/Nov/Report Weeks 47-51.xls l ./Reports 2016/Dec/Report Weeks 47-51.xls ../Nov/Report Weeks 47- +51.xls

But wait! There's more!

relink can also:

  • Convert relative to absolute symlinks with the rel2abs command
  • Show you what it's doing (in color!) with the -v (verbose) option
  • Show you what it would do, without actually performing the operations, with the -n (dry-run) option
  • Resolve chains of symlinks to a certain depth, with the -d option
  • Limit its operations to broken symlinks or non-broken symlinks, with the options -b or -B respectively
  • Limit its operations on links for which a Perl expression returns true, with the -t CODE option, e.g. -t '/regex/' where $_ is the result of readlink on the link. For example, the following will list only those symlinks which are in ~/Documents and whose targets, when fully resolved, point to files also in the ~/Documents directory (the special variables $FULL and $PATHS are documented in the POD).

    ~$ relink list -t '$FULL=~/^$PATHS/' -- ~/Documents /home/foobar/Documents/Reports 2016/Nov/Report Weeks 43-46.xls -> ../O +ct/Report Weeks 43-46.xls /home/foobar/Documents/Reports 2016/Dec/Report Weeks 47-51.xls -> ../N +ov/Report Weeks 47-51.xls
  • Simply show you a (colorized) list of symlinks:

    ~/Documents$ relink list ./Reports 2016/Nov/Report Weeks 43-46.xls -> ../Oct/Report Weeks 43-46 +.xls ./Reports 2016/Dec/Report Weeks 47-51.xls -> ../Nov/Report Weeks 47-51 +.xls
  • And last but not least, it can walk chains of symlinks:

    ~$ relink list -l `which x-www-browser` /usr/bin/x-www-browser -> /etc/alternatives/x-www-browser -> /usr/bin/ +firefox -> /usr/lib/firefox/firefox.sh

You can learn about all of the above features (and a few more!) from the POD, with perldoc relink.

Regards,
-- Hauke D