2

I have files in a directory structure like this:

/books/a.epub
/books/sub/b.epub
/books/test/c.epub

I would like to find all .epub files in root (/books/) and subdirectories (/books//) and use rsync to copy only *.epub files to a remote directory.

So far I tried:

rsync -av  --include='*.epub' --include='*/' --exclude='*' /media/share/Books/ /home/user/docker/calibre/books/

But it is syncing directories as well.

Output should be look like this:

/a.epub
/b.epub
/c.epub
user66638
  • 147

3 Answers3

2

If you have find and xargs available, you could do this:

find . -type f -iname "*.epub" -print0 | xargs -0 -I {} rsync -aPEmivv "{}" "/destination/directory"

And if you don't want to use xargs:

find . -type f -iname "*.epub" -exec rsync -aPEmivv "{}" "/destination/directory" \;

(both variants are to be run inside your /books/ directory)

whether, the -a option in rsync is what you want, as that implies -rlptgoD (recursive, symlinks, pres. permissions, preserve times, preserve owner, preserve group, preserve device files), is your decision, though.

Note that when piping file names to another program, it is advisable to use null-delimited strings (-print0 switch in find, and -0 in xargs and rsync for null-delimited input), so that special characters in filenames don't become problematic.

polemon
  • 11,431
1

Do you have multiple levels to iterate through? If not?

rsync -aP  books/*/*.epub books/*.epub /home/user/movedbooks/.
0

The rsync utility can read pathnames from a file or an input stream. You may use find to find the files you'd like to copy and then copy these with rsync.

We will be assuming that you'd like to recursively search the directory /media/share/Books for any regular file whose name matches the shell globbing pattern *.epub. Any found file matching these criteria should be copied to the single directory /home/user/docker/calibre/books without creating the corresponding directory structure found at the source hierarchy:

find /media/share/Books -type f -name '*.epub' -print0 |
rsync   --from0 --files-from=-  \
        --archive --no-relative \
        / /home/user/docker/calibre/books

The way we're using find above assumes that it supports the non-standard but commonly implemented -print0 predicate. If it does not, you may replace -print0 with -exec printf '%s\0' {} +.

Note the options used with rsync above. The --from0 option makes rsync interpret the stream of data read with --files-from as a nul-delimited list of pathnames. The - used with --files-from means "read from the standard input stream," i.e., from the find command. We use the --archive option (-a) to copy/sync as much metadata as possible with each file and --no-relative (--no-R) to discard the directory path component of the read pathnames.

The source directory used with rsync is / since the pathnames that we read from find are relative to the root directory.

This pipeline executes rsync a single time and safely passes the pathnames between find and rsync. You only need to be aware that rsync will resolve name collisions by simply ignoring other files with the same names as files already listed.


You could also call rsync directly from find using -exec, but to make it a bit more efficient, we can use find to find directories and then call rsync to sync all the *.epub files in each.

find /media/share/Books -type d -exec sh -c '
        for dirpath do
                rsync   --archive       \
                        --include="*.epub" --exclude="*"        \
                        "$dirpath/" /home/user/docker/calibre/books
        done' sh {} +

We're doing something similar here to what you tried but explicitly disallowing recursion by excluding directories from being processed. Instead of recursing with rsync we are outsourcing that to find.

Kusalananda
  • 333,661