I'm trying to replicate select files from a directory structure as symlinks in a duplicate directory, eg

from a directory structure such as this:


make this:

../2/a/file1.txt -> ../../1/a/file1.txt
../2/a/b/file2.txt -> ../../2/a/b/file1.txt

eg keep the dir tree and skip files matching various patterns.

when I use GNU cp from the target directory (../2/a) , replacing relative paths, everything works like I want it, except that it copies data1.txt:

gcp -rs $(cd ../..; pwd)/1/a/* $PWD/

but when I use this in find + xargs to exclude certain files,

find  -X -f $(cd ../..; pwd)/1/a \! -name '*data*' -type f | xargs -t -n1 -I % gcp -s -t $PWD/ %

I get this:

../2/a/file1.txt -> ../1/a/file1.txt
../2/a/file2.txt -> ../1/a/b/file2.txt

eg the directory structure is not being replicated. I can imagine tedious work-arounds but I must be missing something essential in the syntax of xargs. (I want to use find because files before and after certain dates get different treatments.)

my solution was to call subshell, construct paths, make directories, make links...roughly (eg. not tested using the example here):

find $(cd ../..; pwd) -type f \! -name '*data*' -print0 | xargs -0 bash -c 
     'while [ -n "$1" ]; do
           TMP="${1%$(basename $1)}"; 
           mkdir -p "${TMP/1/2}";  
           gcp -s "$1" "${1/1/2}"; 
      done;' "bash"

since this was called inside a script, before the command I exported the relevant path names ("1" and "2" here were actually $PATH1, $PATH2; confusing in this example b/c of $1 vs "1" and "2").

  • Please don't put your answer in the question. Instead, make it an answer. (You can accept your own answer too, if it's the one that worked best for you.) Commented Oct 9, 2018 at 6:51

1 Answer 1


If you don't copy directories recursively with the -r argument, cp won't create any intermediate directories for you. (just adding -r won't work; it has no effect unless the source is a directory).

You could try the -r option of GNU ln ("make relative links"), and create the intermediate directories yourself with mkdir -p {dirname}. Example:

# usage mklinks from to [opts/predicates for find ..]
    from=$1; shift
    to=$1; shift
    find "$from" -type f "$@" -print0 | from=$from to=$to gxargs -0 sh -c '
        for a; do
            t="$to/${a#$from}"; b=${t##*/}
            mkdir -vp "${t%$b}" && gln -vrs "$a" "$t"
    ' sh

mklinks ../../1/a . \! -name '*data*'

Of course, if you're on linux, change gxargs to xargs and gln to ln.

  • didn't work out of the box, but the construct xargs...bash -c was the right direction. for me, it was useful to export the variable inside the script, so that subshells could have them.
    – esar
    Commented Oct 9, 2018 at 6:12
  • I've fixed the example -- when I removed to 'untested' warning, I didn't bother to change the example to the tested one ;-)
    – user313992
    Commented Oct 10, 2018 at 7:04

You must log in to answer this question.

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