I have a directory ~/tmp/foo/ that's populated with subdirectories, files, and symbolic links.

$ tree ~/tmp/foo/
├── eggs
│   ├── baz
│   │   └── link3.txt -> /home/me/file3.txt
│   └── link2.txt -> /home/me/file2.txt
├── hello.txt
├── link1.txt -> /home/me/file1.txt
└── spam
    ├── link4.txt -> /home/me/file4.txt
    └── link5.txt -> /home/me/file5.txt

3 directories, 6 files

I want to recursively copy all the symbolic links under ~/tmp/foo/ as files (as if I'd used cp -rH) to another (nonempty) directory ~/bar/. Is there a simple way to do this?

I've tried the following:

find ~/tmp/foo/ -type l -print | rsync -avzL --files-from=- ~/tmp/foo/ ~/tmp/bar/

But this fails.

  • 1
    I'm not sure exactly what you mean by "as files" -- in the copied version, do you want symlinks, or files (as if you'd used cp -rH)? – Dan Jan 11 '18 at 23:19
  • 1
    Yes, I want files as if I'd used cp -rH. – Brian Fitzpatrick Jan 12 '18 at 0:37

Make sure your find path is "." and relative to your rsync path because find includes the path prefix in the results. Also read the error messages of rsync it will warn you if you get it wrong with something like;

rsync: link_stat "/test/a/a/1/3.txt" failed: No such file or directory (2)

Here is an example that worked for me:

mkdir test
cd test
mkdir a b 
cd a
mkdir 1
echo 1 > 1/2.txt
ln -s 1/2.txt 1/3.txt
tree ../
├── a
│   └── 1
│       ├── 2.txt
│       └── 3.txt -> a/1/2.txt
└── b


find . -type l | rsync -av --files-from=- ./ ../b/
building file list ... done
1/3.txt -> a/1/2.txt

sent 99 bytes  received 25 bytes  248.00 bytes/sec
total size is 9  speedup is 0.07
tree ../
├── a
│   └── 1
│       ├── 2.txt
│       └── 3.txt -> a/1/2.txt
└── b
    └── 1
        └── 3.txt -> a/1/2.txt

4 directories, 3 files

If by failed you mean the -L command ( changing a/1/2.txt to b/1/2.txt ) makes it not work then ... I'm not sure why that is, but I would work around it with mount --bind --make-slave.

| improve this answer | |

If you don't mind copying the regular file foo/hello.txt, you can use:

$ cp -RLv ~/foo/ ~/bar/

The -L switch instructs cp to dereference all symbolic links found in the tree. It differs from -H switch, which instructs dereference of symbolic links from command line arguments only.

However, if you intend to copy the links and the symlinks exclusively, the only idea I have got was making your command line suggestion a little more complicated:

$ ( cd ~/tmp/foo/ ; find -mindepth 1 '(' -type l -o -type d ')' -print | cut -b 3- ) | rsync -avzL --files-from=- ~/tmp/foo/ ~/tmp/bar/

The idea is:

  • The find parameters are modified to return symlinks and directories and to return paths one level down (so it does not return the '.' entry)

  • The --files-from= rsync parameter requires relative paths. So I had to force the find command to run within the source directory, by using ( cd ~/tmp/foo ; find )

  • The cut -b 3- cuts the ./ prefix from the find output.

| improve this answer | |

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.