Shells keep track of symbolic links as a convenience for users. This has the nice effect that cd foo && cd ..
always goes back to the original directory, even when foo
is a symbolic link to a directory. It has two kinds of downsides: the main one is that other programs don't behave this way; additionally, symbolic directory tracking introduces problems of its own (what happens when the symbolic link changes? what happens if the process doesn't have the permission to read the symlink? etc.).
Shells can only do this because they keep track of all directory changes, so they remember how you got there. When a new process starts, it doesn't get this historical information. Under the hood, it finds where it is by moving upwards from the current directory, following ..
links until it hits the root¹.
If you reached a directory through symbolic links, you can print out a symlink-less path with the pwd
builtin, by calling pwd -P
. If you call another program from the shell, don't pass it a path that contains ..
after symlink components, as the program would interpret it differently. Instead, eliminate the ..
components by calling pwd -P
:
cp "$(cd .. && pwd -P && echo /)test-parent.txt" .
If you want to forget about symlinks used in past cd
commands in a shell session, you can run
cd "$(pwd -P && echo /.)"
This changes to what is already the current directory, so it doesn't effectively change the shell process's current directory, but it changes the path that the shell has tracked for the current directory, making it symlink-less.
¹ This is how the getcwd
call traditionally operates. Some kernels do keep track of the current directory, but don't track symbolic links, for backward compatibility if for no other reason (but also because of the subtle edge cases with symbolic links).
pwd
and/bin/pwd
to be relevant.