Delete all folders inside a folder except one with specific name
This will delete all folders inside ./myfolder
except that ./myfolder/test2
and all its contents will be preserved:
find ./myfolder -mindepth 1 ! -regex '^./myfolder/test2\(/.*\)?' -delete
How it works
find
starts a find command../myfolder
tells find to start with the directory./myfolder
and its contents.-mindepth 1
not to match./myfolder
itself, just the files and directories under it.! -regex '^./myfolder/test2\(/.*\)?'
tells find to exclude (!
) any file or directory matching the regular expression^./myfolder/test2\(/.*\)?
.^
matches the start of the path name. The expression(/.*\)?
matches either (a) a slash followed by anything or (b) nothing at all.-delete
tells find to delete the matching (that is, non-excluded) files.
Example
Consider a directory structure that looks like;
$ find ./myfolder
./myfolder
./myfolder/test1
./myfolder/test1/dir1
./myfolder/test1/dir1/test2
./myfolder/test1/dir1/test2/file4
./myfolder/test1/file1
./myfolder/test3
./myfolder/test3/file3
./myfolder/test2
./myfolder/test2/file2
./myfolder/test2/dir2
We can run the find command (without -delete
) to see what it matches:
$ find ./myfolder -mindepth 1 ! -regex '^./myfolder/test2\(/.*\)?'
./myfolder/test1
./myfolder/test1/dir1
./myfolder/test1/dir1/test2
./myfolder/test1/dir1/test2/file4
./myfolder/test1/file1
./myfolder/test3
./myfolder/test3/file3
We can verify that this worked by looking at the files which remain:
$ find ./myfolder
./myfolder
./myfolder/test2
./myfolder/test2/file2
./myfolder/test2/dir2
Using bash:
shopt -s extglob
rm -r myfolder/!(test2)/
Example:
$ tree myfolder/
myfolder/
├── test1
│ └── file1
├── test2
│ └── file2
└── test3
└── file3
$ echo rm -r myfolder/!(test2)
rm -r myfolder/test1 myfolder/test3
$ rm -r myfolder/!(test2)
$ tree myfolder/
myfolder/
└── test2
└── file2
1 directory, 1 file
tl;dr
find ./myfolder -mindepth 1 -maxdepth 1 -type d -not -name test2 \
-exec echo rm -rf '{}' \;
Remove echo if satisfied with the list of files.
Using -mindepth 1
will ensure that the top directory is not selected.
$ find ./myfolder -mindepth 1 -type d
./myfolder/test2
./myfolder/test2/one
./myfolder/test2/two
./myfolder/test
./myfolder/test/a1
./myfolder/test/a1/a2
./myfolder/test/a1/a2/a3
But a -not -name test2
will not avoid subdirs inside test2
:
$ find ./myfolder -mindepth 1 -type d -not -name 'test2'
./myfolder/test2/one
./myfolder/test2/two
./myfolder/test
./myfolder/test/a1
./myfolder/test/a1/a2
./myfolder/test/a1/a2/a3
To do that, you need something like prune:
$ find ./myfolder -mindepth 1 -name test2 -prune -o -type d -print
./myfolder/test
./myfolder/test/a1
./myfolder/test/a1/a2
./myfolder/test/a1/a2/a3
But do not use delete
, as it implies depth
and that will start erasing from the longest path:
$ find ./myfolder -depth -mindepth 1 -name test2 -prune -o -type d -print
./myfolder/test/a1/a2/a3
./myfolder/test/a1/a2
./myfolder/test/a1
./myfolder/test
Either use rm -rf
(remove the echo
if you want to actually erase):
$ find ./myfolder -mindepth 1 -name test2 -prune -o -type d -exec echo rm -rf '{}' \;
rm -rf ./myfolder/test
rm -rf ./myfolder/test/a1
rm -rf ./myfolder/test/a1/a2
rm -rf ./myfolder/test/a1/a2/a3
Or, also use maxdepth
if all you need is to delete directories (and everything inside) (remove the echo
to actually erase):
$ find ./myfolder -mindepth 1 -maxdepth 1 -type d -not -name test2 -exec echo rm -rf '{}' \;
rm -rf ./myfolder/test
A -delete
will still fail if the directory is not empty:
$ find ./myfolder -mindepth 1 -maxdepth 1 -type d -not -name test2 -delete
find: cannot delete ‘./myfolder/test’: Directory not empty