Shell – cd to directory of a symbolically linked file

cd-commandshellsymlinkzsh

Before I write a script, anyone know an easy way to do the following:

$ pwd
/foo/bar
$ ls -l
lrwxr-xr-x  1 username  admin  48 Apr 17  2012 foo.sh -> /bar/foo.sh
$ cd /bar
$ ls
foo.sh

i.e., in the directory /foo/bar, I'd like to do something like cdl (cd link), which would take me to the directory of the linked file (or alternatively to the linked directory, if that happened to be the case—if it was I could type cd -P /bar).

Best Answer

In zsh, there's a modifier for that, or rather two: A to resolve symbolic links (with realpath) and h to extract the “head” (i.e. the dirname).

cd $file(:A:h)

This only works if the symbolic isn't broken. If there is a chain of symbolic links, it is followed until the ultimate target. If the directory was reached through a symbolic link, you'll be in its target (as with cd -P).


Without zsh, if you have the readlink utility, and you want to change to the directory containing the target of the symbolic link:

cd -- "$(dirname -- "$(readlink -- "$file")")"

The target of the link could be itself a symlink. If you want to change to the directory containing the ultimate target of the link, you can call readlink in a loop:

while [ -L "$file" ]; do
  target=$(readlink -- "$file")
  while case $target in */) target=${target%/};; *) false;; esac; done
  case $target in
    */*) cd -- "${target%/*}"; target=${target#**/};;
  esac
done

On Linux, assuming the symlink isn't broken, you can use readlink -f to canonicalize the path:

t=$(readlink -f -- "$file")
cd "${t%/*}"