How is .. (dot dot) resolved in bash when cwd is a symlink to a directory

Consider the following folder structure:

.
├── test1
│   ├── nested1
│   ├── testfile11
│   └── testfile12
└── test2
    ├── nested1 -> /path/to/dir/test1/nested1
    └── testfile21

test2/nested1 is a symlink to the directory test1/nested1. I would expect, if it were the cwd, .. would resolve to test2. However, I have noticed this inconsistency:

$ cd test2/nested1/
$ ls ..
nested1  testfile11  testfile12
$ cd ..
$ ls
nested1  testfile21

touch also behaves like ls, creating a file in test1.

Why does .. as an argument to cd refer to the parent of the symlink, while to (all?) others refers to the parent of the linked dir? Is there some simple way to force it to refer to paths relative to the symlink? I.e. the “opposite” of readlink?

# fictional command
ls $(linkpath ..)

EDIT: Using bash

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

The commands cd and pwd have two operational modes.

  • -L logical mode: symlinks are not resolved
  • -P physical mode: symlinks are resolved before doing the operation

The important thing to know here is that cd .. does not call the syscall chdir("..") but rather shortens the $PWD variable of the shell and then chdirs to that absolute path.

If you are in physical mode, this is identical to calling chdir(".."), but when in logical mode, this is different.

The main problem here: POSIX decided to use the less safe logical mode as default.

If you call cd -P instead of just cd, then after a chdir() operation, the return value from getcwd() is put into the shell variable $PWD and a following cd .. will get you to the directory that is physically above the current directory.

So why is the POSIX default less secure?

If you crossed a symlink in POSIX default mode and do the following:

ls ../*.c
cd ..
rm *.c

you will probably remove different files than those that have been listed by the ls command before.

If you like the safer physical mode, set up the following aliases:

alias cd='cd -P'
alias pwd='pwd -P'

Since when using more than one option from -L and -P the last option wins, you still may be able to get the other behavior.

Historical background:

The Bourne Shell and ksh88 did get directory tracking code at the same time.

The Bourne Shell did get the safer physical behavior while ksh88 at the same time did get the less safe logical mode as default and the options -L and -P. It may be that ksh88 used the csh behavior as reference.

POSIX took the ksh88 behavior without discussing whether this is a good decision.

BTW: some shells are unable to keep track of $PWD values that are longer than PATH_MAX and drive you crazy when you chdir into a directory with an absolute path longer than PATH_MAX. dash is such a defective shell.

Method 2

An important paper to read is http://doc.cat-v.org/plan_9/4th_edition/papers/lexnames where Rob Pike talks about this issue.

You also need to specify your shell, for instance bash tracks it’s idea of the current directory and if you say cd .. it just cuts off the last component and changes there, rather than actually issuing a chdir("..") system call.

Sorry on my phone at the moment, will edit later…

Method 3

You might want to give -p a try, which will follow the physical directory structure without following symbolic links

http://linuxcommand.org/lc3_man_pages/cdh.html


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x