Remove all files/directories except for one file
I have a directory containing a large number of files. I want to delete all files except for file.txt . How do I do this?
There are too many files to remove the unwanted ones individually and their names are too diverse to use * to remove them all except this one file.
Someone suggested using
rm !(file.txt)
But it doesn’t work. It returns:
Badly placed ()'s
My OS is Scientific Linux 6.
Any ideas?
POSIXly:
find . ! -name 'file.txt' -type f -exec rm -f {} +
will remove all regular files (recursively, including hidden ones) except any files called file.txt
. To remove directories, change -type f
to -type d
and add -r
option to rm
.
In bash
, to use rm -- !(file.txt)
, you must enable extglob:
$ shopt -s extglob
$ rm -- !(file.txt)
(or calling bash -O extglob
)
Note that extglob
only works in bash
and Korn shell family. And using rm -- !(file.txt)
can cause an Argument list too long
error.
In zsh
, you can use ^
to negate pattern with extendedglob enabled:
$ setopt extendedglob
$ rm -- ^file.txt
or using the same syntax with ksh
and bash
with options ksh_glob
and no_bare_glob_qual
enabled.
On my Scientific Linux 6 OS this works:
shopt -s extglob
rm !(file.txt)
I also have Debian 32bit installed on a Virtual Machine. The above does not work but the following does:
find . -type f ! -name 'file.txt' -delete
Maintain a copy, delete everything, restore copy:
{ rm -rf *
tar -x
} <<TAR
$(tar -c $one_file)
TAR
In one line:
{ rm -rf *; tar -x; } <<< $(tar -c $one_file)
But that requires a shell that supports here-strings.
you’re all overthinking this.
cd ..
mv fulldir/file.txt /tmp/
rm -rf fulldir
mkdir fulldir
mv /tmp/file.txt fulldir/
Done.
EDIT Actually, easier:
cd ..
ln fulldir/file.txt ./
rm -rf fulldir
mkdir -p fulldir
mv file.txt fulldir/
Another take in a different direction (iff there are no spaces in file names)
ls | grep -xv "file.txt" | xargs rm
or (works even if there are spaces in file names)
ls | grep -xv "file.txt" | parallel rm
from man grep
:
-v, --invert-match
Invert the sense of matching, to select non-matching lines. (-v is specified by POSIX)
-x, --line-regexp
Select only those matches that exactly match the
whole line. For a regular expression pattern, this
is like parenthesizing the pattern and then
surrounding it with ^ and $.
Without the -x
we’d keep my-file.txt
as well.
Use rm !("file.txt")
instead of rm !(file.txt)
Just to give a different answer, you can use the default behavior of rm
that it won’t delete folders:
mkdir tmp && mv file.txt tmp # create tmp dir and move files there
rm # delete all other files
mv tmp/* . && rm -rf tmp # move all files back and delete tmp dir
I find that this approach is very simple, works, and doesn’t require any special extensions (that I know of!)
ls --hide=file.txt | xargs -d 'n' rm