9

Please, try to run the following script:

#!/bin/bash
mkdir a
mkdir a/asub
mkdir b
ln -sfr a/asub b/bsub
touch a/this_file_is_in_a
touch b/this_file_is_in_b
cd b/bsub
echo "I'm in directory: $(pwd)"
echo This is the contents of the upper directory:
ls ..

What would you expect to get? Probably the contents of directory b. However, it does not work that way. I've run it in the directory /tmp/bb.

Below is the result:

wzab@WZabHP:/tmp/bb$ ./ls_tester.sh
I'm in directory: /tmp/bb/b/bsub
This is the contents of the upper directory:
asub  this_file_is_in_a

So despite pwd returning the right path being b the sub-directory, the ".." refers to the a sub-directory. Is my understanding of symlinks incorrect, or is it a bug in Linux?

3
  • 2
    Compare the output of pwd to /bin/pwd. The shell builtin command "lies". Sep 24 at 23:55
  • 1
    "What would you expect to get?" I don't see how the answer could be anything other than a. There's only one subdirectory (asub), and a directory only has one parent.
    – Boann
    Sep 25 at 12:16
  • 2
    @Boann, well, they're showing that pwd prints /tmp/bb/b/bsub, and presumably cd .. goes to /tmp/bb/b/, so yeah, expecting ls .. to also print the contents of /tmp/bb/b/ would seem relatively natural.
    – ilkkachu
    Sep 25 at 13:46
11

Think of directories as little files, essentially lists or tables.

The reason why a directory "knows" its parent directory is because behind the curtains, the full directories are used. As a convenience, links to the upper and current directory exist in every directory.

A symbolic link to a directory is like a link to a file. Once "inside" that linked directory, you're in that linked directory, and the .. and . are hardlinks to the parent and current (real) directory, the information that you've "came via" that symlink (or which one, rather) is technically lost.

When creating a new directory, those three hardlinks are always created, at least on unixoidal systems (there are some older Unices that allowed arbitrary hardlinks though).

However, shells contain a lot of "convenience sugar". The shell will remember for you that actually you came from a symlink, and it'll "rewire" relative directories like that for you.

The shell builtin pwd is also aware of that, works in this manner. The /usr/bin/pwd application will simply state the fully qualified path, i.e. the one that isn't through symlinks. If it didn't, there would be many possible ways to reach that directory, and technically speaking all those combinations (which can be infinite, if you have circular symlinks) would have to be printed.

Most (though probably not all, I don't know for sure) shell's cd command has the option -P, which when entering a directory through a symlink, will use the physical directory structure, rather than following symlinks. You can read about it in the man page of "bash_builtins" (easy to reach through man cd inside bash).

Here's an excerpt from man zshbuiltins:

If the -P option is given or the CHASE_LINKS option is set, symbolic links are resolved to their true values. If the -L option is given symbolic links are retained in the directory (and not resolved) regardless of the state of the CHASE_LINKS option.

(the man page of bash contains a similar section)

You might want to have a look at a really minimal shell, like s. I'm not suggesting you should use it as a daily shell, but it's so minimal, it relies on a lot of OS tools for anything other than the absolute basic things a command interpreter really provides for. It is a pretty good teaching tool in that regard.

4
  • 3
    . and .. are never symlinks (if they exist at all). Also, it's not actually clear what you're trying to say with "behind the curtains, the full directories are used". There's no technical basis for that.
    – zevzek
    Sep 25 at 12:47
  • Consider what happens when the cwd you're in is hidden by another mount: mkdir -p a/A b/B; cd a; mount -B ../b .; /usr/bin/pwd; ls . ; ls /tmp/a -- if the full directory of . (/tmp/a) were used behind the scenes for ., then . and /tmp/a would have the same content. Yet they don't.
    – zevzek
    Sep 25 at 13:23
  • @zevzek, the full directories as readdir() returns them, I suppose? As opposed to the shell's idea of tracking which direction the user went there from. Obviously mounts will fix the directory links both ways along the mount point, otherwise they wouldn't work.
    – ilkkachu
    Sep 25 at 13:44
  • @zevzek I checked the manual and corrected myself. Of course those aren't symlinks, I don't know why I've written it like that. Also
    – polemon
    Sep 25 at 15:31

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