Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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

share|improve this question
    
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?) –  200_success Jun 16 '13 at 8:23

3 Answers 3

up vote 5 down vote accepted

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%/*}"
share|improve this answer
    
Excellent answer: cd $file(:A:h) is exactly what I was looking for! –  vrtcl1dvoshun Jun 18 '13 at 4:20

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$ 
share|improve this answer
1  
N.B. this is a perfect example of why to use $() instead of backticks. –  Kevin Jun 15 '13 at 23:13

one line:

cd $(dirname $([ -L $0 ] && readlink -f $0 || echo $0))
share|improve this answer
    
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 at 3:04

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.