8

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).

1
  • May I ask what your motivation is for doing this? Also, do you want to resolve symlinks recursively? (If /bar is itself a symlink, would you want to follow it?) Jun 16, 2013 at 8:23

3 Answers 3

10

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%/*}"
1
  • Excellent answer: cd $file(:A:h) is exactly what I was looking for! Jun 18, 2013 at 4:20
7

You can use readlink to resolve the symbolic link and then dirname to get its directory.

cdl () {
    cd "$(dirname "$(readlink "$1")")"; 
}
bash-3.2$ pwd
/foo/bar
bash-3.2$ ls -l
total 8
lrwxr-xr-x  1 root  wheel  11 Jun 15 19:10 foo.sh -> /bar/foo.sh
bash-3.2$ cdl foo.sh 
bash-3.2$ pwd 
/bar
bash-3.2$ 
1
  • 1
    N.B. this is a perfect example of why to use $() instead of backticks.
    – Kevin
    Jun 15, 2013 at 23:13
0

one line:

cd $(dirname $([ -L $0 ] && readlink -f $0 || echo $0))
1
  • This is your second one-line answer (it even appears to be exactly the same answer). Again, one-line answers are not the most helpful. Please expand your answers to include more helpful information and explanation (including supporting links and documentation).
    – HalosGhost
    Aug 29, 2014 at 3:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .