3

Suppose I have created two folders in /tmp called parent and child. child contains a file called test-child.txt and parent contains a file called test-parent.txt. Now let's go inside the parent and create a symbolic link to the child. Next, go inside the child and try to copy the test-parent.txt from the parent. The bash completion works but the actual file copy fails --

cd /tmp
pwd
/tmp
mkdir parent
mkdir child
touch child/test-child.txt
ls child/
test-child.txt
cd parent
ln -sf ../child .
touch test-parent.txt
cd child
cp ../test-parent.txt .
cp: cannot stat ‘../test-parent.txt’: No such file or directory 

why ??

Moreover, when I am inside child and if I say --

pwd
/tmp/parent/child
1

2 Answers 2

6

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

4

It is because cp isn't playing the same link game your shell is. Your shell is tracking the links to the current working directory as an indirection to the current working directory, but the kernel doesn't want any of that nonsense when the shell goes to call up cp - rather the kernel will make the cp's current working directory a fully qualified absolute path - and so its . links and your shell's . links are not the same ..

You can ensure an absolute path to the current working directory as easily as cd -P . and the same will work for any other directory like cd -P -- /any/other/directory. You can at least use pwd to print the absolute path to . like pwd -P, and in many shells it will alter $PWD to the absolute path as well (in the same way cd -P . does).

For example:

cd /tmp
mkdir -p parent child
cd parent
ln -s ../child child
touch ./child/somefile
cd -L child                  ###the default
printf %s\\n "$PWD"/*
cd -P .
printf %s\\n "$PWD"/*

OUTPUT

/tmp/parent/child/somefile
/tmp/child/somefile
1
  • thanks for the explanation, actually my real problem was with java directory search path, the java program that I was trying to run fetches files from the parent folder and the codes are all in the child (which is a symlink) folder, and this kind of ambiguity is messing up the execution. I thought there should be some way to make a symlink that will respect relative path, but looks like it's not.
    – ramgorur
    Mar 18, 2015 at 3:56

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .