5

Consider the following setup:

~/Desktop/Public/subdir
~/Desktop/subdir --> ~/Desktop/Public/subdir (symbolic link)

Now I do:

cd ~/Desktop/subdir

Which leads me into the linked directory.

If I now issue the command:

cd ..

I will be moved back into the Desktop directory. This means the cd command is context sensitive - it remembers that I entered subdir via the symbolic link.

However, issuing the command

cp testfile ..

will copy testfile into Desktop/Public.

I prefer the behavior of cd over the (often unpredictable) behavior of cp. In any case, I wonder what is the reason for this difference in behavior? Just a legacy thing or is there a good reason?

  • Btw., behaviour and behavior are both correct spellings. – Konstantin Schubert Jul 23 '13 at 10:38
3

The reason that cd is aware that you have entered the directory via a symlink is because it is built-in to the shell. The cp command is an external binary and is only passed data via command line arguments and environment variables. Environment variables do not contain data on how you entered the current directory.

If you are using bash, you can make cd function the same as cp if you want things to be consistent. set -P will accomplish this. From the manpage:

          -P      If  set,  the shell does not follow symbolic links when executing commands such as cd that change the current working directory.  It uses the physical directory structure instead.  By default, bash follows the
                  logical chain of directories when performing commands which change the current directory.
  • 4
    Actually, the PWD environment variable does contain data on how you entered the current directory. cp just doesn't use it like Bash does. – cjm Jul 22 '13 at 14:38
  • Wouldn't it be nice if it did? I understand that there are legacy issues, but I think it would be much more user friendly if cp did consider the fact that I entered via a symbolic link. – Konstantin Schubert Jul 22 '13 at 17:13
  • 1
    @Konstantin Only shells set PWD. So if a shell starts program1 which starts program2, then program2 has no idea whether $PWD is still up-to-date. cp has no way to know that you entered through a symbolic link, this tracking is internal to the shell. – Gilles 'SO- stop being evil' Jul 22 '13 at 21:46
2

When cd works as described it can only be a trick of cd or your shell. .. is an entry to the parent directory. In your case the parent of subdir can only be Public and not Desktop. Thus cp does the right think and you will get the same behavior with redirects (like >).

The cd example only uses some "user-friendly" trick to behave like it does. See https://stackoverflow.com/questions/10456784/behavior-of-cd-bash-on-symbolic-links

If you really need the functionality you have to use something like

cp testfile "${PWD##*/}"

(copy to the parent directory of the current directory, according to shell tracking). In zsh, you can simplify this to cp testfile $PWD:h

1

It's rather the opposite: cp behaves like any other application, it interprets .. as the parent directory of the current directory. That's because the kernel interprets .. as the parent directory of the current directory.

When you run cd through a symbolic link, the path that you pass to cd does not make the parent directory of the destination directory apparent. When you run cd ~/Desktop/subdir, the destination is not /home/konstantin/Desktop/subdir — that's a symbolic link. The destination directory is /home/konstantin/Desktop/Public/subdir. (Or, to be pedantic: the destination directory is a subdirectory called subdir of a subdirectory called Public of … of a subdirectory called home of the root directory.) /home/konstantin/Desktop/subdir/.. is not /home/konstantin/Desktop: the property that the parent directory of /…stuff…/subdir is /…stuff… only holds in the absence of subdirectories.

Because it's often convenient to think of symbolic links to directories as if they were directories, shells perform symbolic link tracking. When you run cd, the shell remembers which path (possibly using symbolic links) you've used to reach the destination. And when you use .. in an argument to cd (or similar shell builtins such as pushd), the shell performs a textual interpretation of .. rather than interpreting it as the current directory: cd /some/stuff/../more is transformed to cd /some/more. That way, cd behaves as if the symbolic links were actually directories.

Textual interpretation of .. is known as logical directory tracking, and filesystem interpretation of .. is known as physical directory tracking. If you want to use physical directory tracking (i.e. to turn off textual interpretation of ..), pass the -P option to cd. The -L option forces logical tracking on, in case it's been disabled. Logical tracking is off by default; you can turn it off with set -P in bash or with setopt chase_links in zsh.

The command pwd displays the current directory as tracked by the shell. Like cd, you can pass the option -L or -P to force logical or physical tracking.

All this is happening inside the shell. External applications such as cp have no way to know what tracking the shell is doing internally. So if you want to use logical directory tracking in an argument to a command, you need to convert that first to a path that doesn't depend on logical tracking.

The shell variable PWD keeps track of the current directory ($PWD contains the same string that pwd prints). If you want to strip off the last textual component of the tracked path to the current directory, instead of appending /.. which only works with logical tracking, you can use a textual method.

cp testfile "${PWD##*/}"

or in zsh:

cp testfile $PWD:h

If you want to type .. and not have to think about expressing the directory differently, run cd to the desired target directory, and then use the path to the destination in the argument of the command.

cp testfile "$(cd .. && pwd)"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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