0

I am attempting to copy a single specific file that is contained within 960 sub directories in a parent directory. Each file has the same name but has unique content.

I want to maintain the hierarchy of the directories and names in the new parent directory, but only want one specific file rather than all the files in each sub directory.

I also need to create each of these sub directories named processorN where N is a number from 0 to 959.

Any ideas, this has stumped me and I can't seem to find a good solution that isn't going to take me hours.

CentaurusA
  • 2,692

2 Answers2

3

rsync can do that with some tricky --include and --exclude flags:

rsync -a -v --include="*/" --include="specific-file.txt" --exclude="*" from/ to/

This will copy all directories and the specific-file.txt from from to to while skipping everything else. The command will preserve the complete directory structure below from and copy it to to. You may first want to run this with the -n switch to see what would happen, i.e.:

# check:
rsync -a -v -n --include="*/" --include="specific-file.txt" --exclude="*" from/ to/

# actually do:
rsync -a -v    --include="*/" --include="specific-file.txt" --exclude="*" from/ to/
PerlDuck
  • 13,885
1

Normally I use find with process substitution and an array, like this:

# copy all instances of filename from srcroot to destroot,
# preserving directory tree structure

filename="foo.txt" srcroot="/path/to/src" destroot="/path/to/dest"

declare -a files mapfile -t files < <(find "$srcroot" -name "$filename" -type f -printf "%P\n")

for file in "${files[@]}"; do path=$(dirname "$file") mkdir -p "$destroot/$path" cp -a "$srcroot/$file" "$destroot/$path" done

Mapfile seems to be available in bash 4+, but if you need something more portable, or want a one-liner with no arrays, you can use the solution offered by kos in the comments:

find "$srcroot" -name "$filename" -type f -printf "%P\0" | while IFS= read -d '' -r file; do
    path=$(dirname "$file")
    mkdir -p "$destroot/$path"
    cp -a "$srcroot/$file" "$destroot/$path"
done

There are probably faster or more clever solutions (like the rsync answer), but this is quick to code, easy to understand, and easy to add more complex logic if needed.

kos
  • 41,268
Alcamtar
  • 544