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.

I've created symlink with absolute path to the directory (Blink) and have for example following tree:

$ ls -l /tmp/A
total 0
lrwxrwxrwx 1 root root 6 Apr  3 12:27 Blink -> /tmp/B
-rw-r--r-- 1 root root 0 Apr  3 12:27 foo

$ ls -l /tmp/B
total 0
-rw-r--r-- 1 root root 0 Apr  3 12:27 bar

then I go to /tmp/A and change directory to Blink:

$ cd /tmp/A
$ pwd
/tmp/A
$ cd Blink
$ pwd
/tmp/A/Blink

cd .. returns me to /tmp/A but if I type for example ls ../foo I'll got error:

ls: ../foo: No such file or directory

builtin cd command resolve path as needed, but external ls consider the .. as up-level of /tmp/B and therefore cannot find foo.

What is the problem here? Can I get the foo file from /tmp/A/Blink by relative path like ../foo?

share|improve this question
    
The same question is cross-posted on SuperUser –  Peter.O Apr 12 '12 at 20:00

4 Answers 4

up vote 8 down vote accepted

I don't think you can do this. Shells nowadays keep track of how they got where they are, they keep track of current working directory by name, rather than just relying on file system and OS to keep it. That means the built-in cd can go "back up" the symbolic link, rather than just following ".." chains directly.

But when you run ls ../foo, ls isn't aware that the shell got to a directory by following symbolic links. ls will do a stat() on "../foo". The ".." part comes from the current directory, which has only a single ".." entry for it's parent. The whole symbolic link thing doesn't change the way directories have a single "." and a single ".." entry. Symbolic links let the kernel insert an extra layer of indirection into file paths. When you pass "../whatever" to open() or stat(), the kernel just uses the current working directory of the process doing the call to figure out which single directory is named by "..".

share|improve this answer
    
Thank you for detailed explanation –  user478681 Apr 13 '12 at 7:19

You can't do it, because /tmp/A/Blink is actually /tmp/B. So, ls ../A/foo does work.

You may instead find it useful to be able to cd to the real /tmp/B directory, rather than the aliased /tmp/A/Blink. To do that, you can use the following function instead of cd (which you can put into your .bashrc)

lcd() { cd $(readlink -f "$1"); }

lcd, of course, works for bothe normal and symbolic-linked directories.
Note: readlink -f acts on the final target of the link (when links are daisy-chained)..

share|improve this answer
1  
cd -P seems simpler. –  jw013 Apr 12 '12 at 19:26
    
@jw013: You are right; it is. I wasn't aware of it.. thanks.. –  Peter.O Apr 12 '12 at 19:43

The shell stores the current working directory in $PWD. That's what's used for the shell builtins cd and pwd, and it treats symlinks as normal directories, as you've seen. Sometimes this is helpful, sometimes not.

You can find the real directory using pwd (type help pwd for more details):

$ pwd
/tmp/A/Blink
$ pwd -L
/tmp/A/Blink
$ pwd -P
/tmp/B

Likewise, cd has an option -P (again, help cd is your friend):

$ cd /tmp/A/Blink
$ pwd
/tmp/A/Blink
$ cd -P ..
$ pwd -P
/tmp

Finally, you can turn off the "feature" altogether:

$ set -P
$ cd /tmp/A/Blink
$ pwd
/tmp/B
share|improve this answer
    
Thanks, I'm aware about set -P feature, but was confused by ls command behavior... –  user478681 Apr 13 '12 at 7:14
    
Like I said, use set -P and ls behaviour starts to make sense. :) –  ams Apr 13 '12 at 8:42

Bruce and ams gave beautiful answers explaining the behavior behind. But to actually do the ls ../foo you were asking, you could go with something like

ls $(dirname $PWD)/foo
share|improve this answer

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.