Search code examples
shellunixln

Symbolic links to symbolic links... partial canonicalization


Suppose I create a symbolic link "file2" to a file "file1" two directories above the current location (e.g., "ln -s ../../file1 file2". But now suppose that "file1" is also a symbolic link to a file "file0" two directories down from its location (say its relative path is dir1/dir2/file0").

I'd prefer if the "file1" symbolic link contains a relative URL going to file0 "../../dir1/dir2/file0" rather than just "../../file1". That latter only indirectly points to the file.

What's a good way of doing this?

I could hack together something with readlink. But I'm still hoping there's an "better" way that I'm not considering or have overlooked.


Solution

  • This will recursively test a file and retrieve a complete relative path, then remove unnecessary "./" and "foo/../"

    #!/bin/bash
    linkfile="$1"
    while test -L "$linkfile" ; do
    linkfile="$(dirname ${linkfile})/$(readlink ${linkfile})"
    done
    perl -e '$x=shift; while ($x =~ s#//+#/#g) {} ; while ($x =~ s#/\./#/#g) {} ; while ($x =~ s#/([^\.]|[^\.][^/]+?|\.[^\.]+?)/\.\./#/#g) {} ; while ($x =~ s#^([^\.]|[^\.][^/]+?|\.[^\.]+?)/\.\./#/#g) {} ; $x =~ s#^\./##; print "$x\n";' "$linkfile"
    

    If you save that as "canonical.bash", then you use it

    $ ln -s `canonical.bash foo` bar
    

    The regex won't reduce a few cases like "/..x/../" and "../foo/" (if foo/ is in your $PWD), but should be otherwise sturdy.