ln -s

The reason that ln -s file target is confusing is that, even though it is consistent with cp and mv, unlike those commands, it is not consistent english language.

Compare the english sentence:

Copy a file to a place

with the shell command

cp a_file a_place

Notice how they're the same way round? Now compare the english

Create a link thingy to the file boing

In english, we say this the other way round, whereas the shell command is still in the same direction as cp and mv:

ln this_file_exists make_this_point_to_that

and that's why it is confusing you sometimes.

A solution (or two)

I present, for your use, two very simple perl scripts which I've personally used for years to solve this problem. Choose one, stick it in your ~/bin (unless, of course, you've been scared by 'Why putting ~/bin or . in your $PATH is a bad idea').

They both rely on the fact that when you are creating links, you generally want to create a link that doesn't exist, to a file that does. Both these scripts accept the arguments either way round and create the link the way that makes sense. The downside of this is that you can't create a new link over an old one without rm-ing it first.

The first one is dead simple. It checks which way round you meant and passes them off to the real ln program. It will handle hard links or symlinks (with the -s option) but not any other option since they tend to do with overwriting old links, and we can't do that*.


#!/usr/bin/perl

if ($ARGV[0] eq '-s'){
        $s="-s";
        shift;
}

if (-e $ARGV[0]){
        die ("Cannot overwrite existing file") if -e $ARGV[1];
        `ln $s $ARGV[0] $ARGV[1]`;
}else{
        die ("Target file does not exist") unless -e $ARGV[1];
        `ln $s $ARGV[1] $ARGV[0]`;
}
and the other is slightly more interesting in that it does the symlinks itself, without calling the old ln program. The upshot of this is that you could replace the /usr/bin/ln on your system with it. But I really wouldn't recommend it.
#!/usr/bin/perl

use File::Spec("rel2abs");

if ($ARGV[0] eq '-s'){
        shift;
}

if (-e $ARGV[0]){
        die ("Cannot overwrite existing file") if -e $ARGV[1];
        symlink (File::Spec->rel2abs($ARGV[0]),File::Spec->rel2abs($ARGV[1]));
}else{
        die ("Target file does not exist") unless -e $ARGV[1];
        symlink (File::Spec->rel2abs($ARGV[1]),File::Spec->rel2abs($ARGV[0]));
}

* I did play with the idea of checking to see if one file is a symlink (if they both already exist) and overwriting that one, but it seems a big ambiguous and dangerous.
Thanks to OldMiner for helping me sort the confusing nature of my w/u :-)