Search code examples
gitbashln

What is the proper way to make symbolic links with regard to file paths being full or shortened and relying on "token" expansion


Symbolic Links

I am having a hard time understanding symbolic links. I believe I understand the basic premise, it is an alias essentially.

However, does it depend on what working directory you are in when you make it on how it works, assuming you are letting that current working directory play into the making of the symbolic link?

The current command I am using is ln -s

Examples scenarios

I have a few projects in GitHub, some of them are simple little scripts I have written that I store in ~/bin. ~/bin is in my $PATH environment variable for easy access to calling these scripts:

$echo $PATH
/Users/me/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/git/bin

I just noticed that in my install of git it looks like /usr/local/git/bin has been appended to my $PATH. For the time being, I am going to pretend that didn't happen.

For example: ( hist is a simple script that shows stats on my most recently used commands )

#/bin/bash
history | awk '{CMD[$2]++;count++;}END { for (a in CMD)print CMD[a] " " CMD[a]/count*100 "% " a;}' | grep -v "./" | column -c3 -s " " -t | sort -nr | nl |  head -n10

$hist
     1  115  23%    l
     2  58   11.6%  cd
     3  44   8.8%   ls
     4  29   5.8%   git
     5  14   2.8%   sudo
     6  13   2.6%   locate
     7  12   2.4%   open
     8  11   2.2%   wc
     9  10   2%     dt
    10  9    1.8%   rm

If I perform this action:

$cd ~/bin
$pwd
$/Users/me/bin
$ln -s hist ~/Desktop/hist
$ls -la ~/Desktop/
$ls -la | grep hist
lrwxr-xr-x   1 me  staff  4 Sep 15 21:15 hist -> hist
$cd ~/Deskop
$hist

The command words, but the listing of hist -> hist concerns me, it provides no information as to where the real source is. I'm not sure I can move the file elsewhere and it will still work.

So I have recently gotten in the habit of not using any expansion tokens when making symbolic links. Meaning no more ~ and the like.

I only even had to begin doing this as I started using GitHub, and was making repositories of my .bash_profile, .bashrc, and .bash_history. It was becoming a lot of work every time I made a change to my .bash_profile to then cp it from my local repository to ~/bin, and then run source ~/bin/.bash_profile

Questions

What is the proper procedure for this with ln -s, or is it purely a matter of preference, and the end results will always work the same.

The good news is I learned about the -F flag to ln so I no longer get an error if I hit the filename that is the destination. The -F flag will tell ln to write over it, saving me time of mv'ing the file and then running the ln -s command.

Reference

This is what my ~/bin looks like right now, with many removed to keep it simple:

$l
total 56
lrwxr-xr-x  1 me  staff   53 Sep 15 20:40 hist -> /Users/me/Documents/me/git-projects/hist/hist
lrwxr-xr-x  1 me  staff   57 Sep 15 20:41 lcaser -> /Users/me/Documents/me/git-projects/lcaser/lcaser
lrwxr-xr-x  1 me  staff   70 Sep 10 05:12 mate -> /Applications/TextMate 1.x.app/Contents/SharedSupport/Support/bin/mate
lrwxr-xr-x  1 me  staff   56 Sep 15 20:42 tart -> /Users/me/Documents/me/git-projects/tart/tart.sh
lrwxr-xr-x  1 me  staff   69 Sep 15 20:44 watchinstall -> /Users/me/Documents/me/git-projects/watchInstall/watchInstall

Solution

  • Consider:

    ln -s target linkname
    

    The complication occurs only when target is specified by a relative path. If it is, then the path is relative to the directory that holds the file linkname. The currect working directory is always ignored.

    As an example, consider

    ln -s hist ~/Desktop/hist
    

    This command creates a link named hist in ~/Desktop. Since no path is given for the target, the target is interpreted as also being in the directory ~/Desktop. In this case, that means the the link hist points to itself.

    As another example, consider

    cd /var/tmp
    ln -s ../hist ~/Desktop/hist
    

    This will create a link from ~/Desktop/hist to ~/hist because the ../ is interpreted relative to the directory that holds the link, ~/Desktop. The directory that we are in when the ln command is executed and the directory that we are in when we access /Desktop/hist are both irrelevant.

    Another subtlety

    ln does not care what you use for target when the command is issued: it can be arbitrary text. The value of target is not intepreted until an attempt is made to access linkname. Consider:

    $ ln -s "mary had a little lamb" ~/file1
    $ ls -alt ~/file1
    lrwxrwxrwx 1 user group 22 Sep 15 21:47 /home/user/file1 -> mary had a little lamb
    $ cat ~/file1
    cat: /home/user/file1: No such file or directory
    

    If we want we can later create that file:

    $ echo this is a test >~/"mary had a little lamb"
    $ cat ~/file1
    this is a test
    

    Again, the existence of the target is only checked when an attempt is made to access the link.

    Documentation

    This behavior is documented in man ln:

    Symbolic links can hold arbitrary text; if later resolved, a relative link is interpreted in relation to its parent directory.