Linux – Recursively Copy a Directory as Symlinks, Preserving Current Symlinks

bashlinuxsymbolic-link

I would like to make an exact copy of a directory (which contains sub-directories, files, and symlinks) except that I want each file in the target directory to be a symlink back to the original file.

This I can already accomplish using:

cp -sR /home/me/SourceDir TargetDir

However, I would like any relative symlinks within SourceDir that point to somewhere else in SourceDir to remain the same relative links, hence now pointing to TargetDir.

For example, let's say SourceDir contains the following:

File0
Dir1/File1
Dir1/File2
Dir2/File3
Dir2/File2   <-- this is a symlink to ../Dir1/File2

Then I would like TargetDir to look like:

File0        <-- symlink to  ../SourceDir/File0
Dir1/File1   <-- symlink to  ../../SourceDir/Dir1/File1
Dir1/File2   <-- symlink to  ../../SourceDir/Dir1/File2
Dir2/File3   <-- symlink to  ../../SourceDir/Dir2/File3
Dir2/File2   <-- this is still a symlink to ../Dir1/File2, so: TargetDir/Dir1/File2

I wouldn't mind if this had to be accomplished in two or three steps (something I could write into a simple shell script).

One thought I had was first copying all symlinks only, then doing the cp -sR with a no-clobber.

Best Answer

You haven't said you are in /home/me, but your cp command will create all your links as absolute, wherever you are, ie TargetDir/File0 -> /home/me/SourceDir/File0 etc, not the relative links you illustrate.

You can achieve what you want using find:-

cp -sR /home/me/SourceDir TargetDir
cd /home/me/SourceDir
find -type l -exec cp -P "{}" "/Path/To/TargetDir/{}" \;
  • The first command is your original.
  • By positioning yourself in the source directory you eliminate it from the find path.
  • The find with cp -P overwrites the links with verbatim copies of the source links.
  • The double-quotes allow for files and directories with embedded blanks.

If you really want relative links (TargetDir/File0 -> ../../SourceDir/File0 etc) for your normal files, you will need to use find -type f and execute mkdir -p to create the target directory structure, then find -type f and execute ls -s for each file.

Related Question