rnso
rnso

Reputation: 24535

How to copy folders and subfolders which have selected files

I have a directory oridir with structure as follows:

.
├── DIRA
│   ├── DIRA1
│   │   └── file2.txt
│   └── DIRA2
│       ├── file1.xls
│       └── file1.txt
└── DIRB
    ├── DIRB1
    │   └── file1.txt
    └── DIRB2
        └── file2.xls

I have to copy files which have extension .xls while maintaining the directory structure. So I need to have following directory and files in a newdir folder:

.
├── DIRA
│   └── DIRA2
│       └── file1.xls
└── DIRB
    └── DIRB2
        └── file2.xls

I tried following command but it copies all files and folders:

cp -r oridir newdir

Finding required files can be done as follows:

$ find oridir | grep xls$
oridir/DIRB/DIRB2/file2.xls
oridir/DIRA/DIRA2/file1.xls

Also as follows:

$ find oridir -type f -iname *.xls
./oridir/DIRB/DIRB2/file2.xls
./oridir/DIRA/DIRA2/file1.xls

But how to create these folders and copy files. How can I achieve this selected creation of directories and copying files with `bash' in Linux?

Edit: There are space also in some file and directory names.

Upvotes: 1

Views: 699

Answers (3)

Jeff Y
Jeff Y

Reputation: 2456

See Yuji's excellent answer first, but I think tar is also a good option here:

cd oridir; find . -name "*.xls" | xargs tar c | (cd ../newdir; tar x)

You may need to adjust oridir and/or ../newdir depending on the precise paths of your directories.

Possible improvement: Here is a version that may be better in that it will handle files (and paths) with spaces (or other strange characters) in their names, and that uses tar's own options instead of xargs and cd:

cd oridir; find . -print0 -name "*.xls" | tar -c --null -T- | tar -C ../newdir -x

Explanation:

The -print0 and the --null cause the respective commands to separate filenames by the null (ASCII 0) character only.

-T- causes tar to read filenames from standard input.

-C causes tar to cd before extracting.

Upvotes: 1

Yuji
Yuji

Reputation: 525

If a destination dir is "dest".

foo.sh

#!/bin/bash
dest=./dest
find . -type f -name "*.xls" | while read f
do
    d=$(dirname "${f}")                                                         
    d="${dest}/${d}"
    mkdir -p "${d}"
    cp "${f}" "${d}"
done

Make dirs and files.

$ mkdir -p DIRA/DIRA1
$ mkdir -p DIRA/DIRA2
$ mkdir -p DIRB/DIRB1
$ mkdir -p DIRB/DIRB2
$ touch DIRA/DIRA1/file2.txt
$ touch DIRA/DIRA2/file1.xls
$ touch DIRA/DIRA2/file1.txt
$ touch DIRB/DIRB1/file1.txt
$ touch DIRB/DIRB1/file2.xls

A result is

$ find dest
dest
dest/DIRB
dest/DIRB/DIRB1
dest/DIRB/DIRB1/file2.xls
dest/DIRA
dest/DIRA/DIRA2
dest/DIRA/DIRA2/file1.xls

Upvotes: 1

Gonzalo Matheu
Gonzalo Matheu

Reputation: 10064

cp's --parents flag makes use full source file name under DIRECTORY

For example, if recursive glob ** is enabled (shopt -s globstar):

cp --parents origin/**/*.xls target

If recursive glob is not enabled, you have to add a wildcard for each level on directory hierarchy:

cp --parents origin/*/*/*.xls target

Upvotes: 3

Related Questions