one way is to use -exec rm
instead of -delete
.
find a \( -name b -prune \) -o -type f -exec rm {} +
alternatively use -not -path
instead of -prune
:
find a -not -path "*/b*" -type f -delete
Explanation why -prune
collides with -delete
:
find complains when you try to use -delete
with -prune
because -delete
implies -depth
and -depth
makes -prune
ineffective.
observe the behaviour of find with and without -depth
:
$ find foo/
foo/
foo/f1
foo/bar
foo/bar/b2
foo/bar/b1
foo/f2
There is no guarantee about the order in a single directory. But there is a guarantee that a directory is processed before its contents. Note foo/
before any foo/*
and foo/bar
before any foo/bar/*
.
This can be reversed with -depth
.
$ find foo/ -depth
foo/f2
foo/bar/b2
foo/bar/b1
foo/bar
foo/f1
foo/
Note that now all foo/*
appear before foo/
. Same with foo/bar
.
more explanation:
-prune
prevents find from descending into a directory. In other words -prune
skips the contents of the directory. In your case -name b -prune
means that when find reaches a directory with the name b
it will skip the directory including all subdirectories.
-depth
makes find to process the contents of a directory before the directory itself. That means by the time find gets to process the directory entry b
its contents has already been processed. Thus -prune
is ineffective with -depth
in effect.
-delete
implies -depth
so it can delete the contents first and then the empty directory. -delete
refuses to delete non-empty directories.
Explanation of alternative method:
find a -not -path "*/b*" -type f -delete
This may or may not be easier to remember.
This command still descends into the directory b
and proceses every single file in it only for -not
to reject them. This can be a performance issue if the directory b
is huge.
-path
works differently than -name
. -name
only matches against the name (of the file or directory) while -path
is matching against the entire path. For example observe the path /home/lesmana/foo/bar
. -name bar
will match because the name is bar
. -path "*/foo*"
will match because the string /foo
is in the path. -path
has some intricacies you should understand before using it. Read the man page of find
for more details.
Beware that this is not 100% foolproof. There are chances of "false positives". The way the command is written above it will skip any file which has any parent directory which name is starting with b
(positive). But it will also skip any file which name is starting with b
regardless of position in the tree (false positive). This can be fixed by writing a better expression than "*/b*"
. That is left as an exercise for the reader.
I assume that you used a
and b
as placeholders and the real names are more like allosaurus
and brachiosaurus
. If you put brachiosaurus
in place of b
then the amount of false positives will be drastically reduced.
At least the false positives will be not deleted, so it will be not as tragic. Furthermore, you can check for false positives by first running the command without -delete
(but remember to place the implied -depth
) and examine the output.
find a -not -path "*/b*" -type f -depth
I surmise that your external drive uses a filesystem such as VFAT which doesn't allow colons in file names.
A simple option would be to back up your files as archives (zip
, 7z
, tar.xz
, whatever catches your fancy). This way you wouldn't be limited by any characteristic of the filesystem other than the maximum file size.
Another possibility would be to use rdiff-backup, which takes care of translating file names that don't fit on the destination filesystem, as suggested by poolie.
A generic approach to unsupported characters is to leverage the filesystem layer to transform the file names. The FUSE filesystem posixovl transforms file names into names that Windows's VFAT supports.
mkdir ~/mnt
mount.posixovl -S /media/extern_drive ~/mnt
rsync -a /work ~/mnt
fusermount -u ~/mnt
See How can I substitute colons when I rsync on a USB key? for more details, and check that thread for any new solution that may come up.
Best Answer
Facilitate the
--exclude=
option.To sync to a depth of 2 (files within folder and subfolders):
It will give you this:
To sync to a depth of 3 (files within folder, subfolders and subsubfolders):
will give you: