1. Overview
Deleting files in the Bash terminal can be quite a daunting task, especially when we're unsure how to target files. For this same reason, it's usually better to remove files per directory than to delete them across multiple subdirectories unless we are entirely sure about our actions.
In this tutorial, let's look at three great ways to delete all files in a directory except those we still need.
2. Using the find Command
With find, we can use options like -not to help indicate which files shouldn't be deleted and the -delete option to invoke deletion of the rest of the files in the directory:
find [path to directory] -type f -not -name [filename or extension] -delete
For example, let's see how to delete all the files in a test directory except file1.jpg, file2.png, file3.pdf, and file4.zip:
$ ls
'file1 (another copy).jpg' 'file3 (another copy).pdf'
'file1 (copy).jpg' 'file3 (copy).pdf'
file1.jpg file3.pdf
'file2 (another copy).png' 'file4 (another copy).zip'
'file2 (copy).png' 'file4 (copy).zip'
file2.png file4.zip
$
Next, we list the contents of the test directory:
$ ls
file1.jpg file2.png file3.pdf file4.zip
The files we excluded using the find command above are still contained in the directory.
3. Using Extended Globbing and Pattern Matching Operators
Extended globbing is also referred to as extglob. When extglob is enabled, it activates pattern matching operators that can be used to enhance the effectiveness of the rm command. Also, with the ! operator, we can exclude all files we don't want glob to match during deletion.
Let's look at the list of pattern matching operators:
- ?(pattern-list) matches at least zero and at most one occurrence
- *(pattern-list) matches zero, one, or multiple occurrences
- +(pattern-list) matches one or multiple occurrences
- @(pattern-list) matches one pattern occurrence
- !(pattern-list) excludes the given patterns from the matching
To enable extglob for the given Bash session, we can use the shopt built-in:
$ shopt -s extglob
After this, to exclude a file, we use the ! operator:
$ rm -v !("[file]")
Let's refresh the contents of the test directory file1.jpg, file2.png, file3.pdf, and file4.zip:
$ ls
'file1 (3rd copy).jpg' 'file3 (3rd copy).pdf'
'file1 (another copy).jpg' 'file3 (another copy).pdf'
'file1 (copy).jpg' 'file3 (copy).pdf'
file1.jpg file3.pdf
'file2 (3rd copy).png' 'file4 (3rd copy).zip'
'file2 (another copy).png' 'file4 (another copy).zip'
'file2 (copy).png' 'file4 (copy).zip'
file2.png file4.zip
To exclude multiple files or file extensions from the deletion, we use |, also known as a pipe, to separate them:
$ rm -v !("file1.jpg"|"file2.png"|"file3.pdf"|"file4.zip")
removed 'file1 (3rd copy).jpg'
removed 'file1 (another copy).jpg'
removed 'file1 (copy).jpg'
removed 'file2 (3rd copy).png'
removed 'file2 (another copy).png'
removed 'file2 (copy).png'
removed 'file3 (3rd copy).pdf'
removed 'file3 (another copy).pdf'
removed 'file3 (copy).pdf'
removed 'file4 (3rd copy).zip'
removed 'file4 (another copy).zip'
removed 'file4 (copy).zip'
Lastly, we can use extglob to exclude files using their file type, such as .png:
$ rm !(*.png)
As a result, the code snippet above removes all the files in a directory except files with the .png file type.
Notably, when not using extended globbing, it's usually better to disable it:
$ shopt -u extglob
This way, we prevent unintended interpretation of commands.
4. Using the Bash GLOBIGNORE Variable
GLOBIGNORE specifies data or patterns that glob shouldn't match.
Let's set the GLOBIGNORE variable with the files we would like to exclude from the deletion.
$ GLOBIGNORE=file1.jpg:file2.png:file3.pdf:file4.zip
With a directory structure equivalent to the one in the previous section, the command below deletes all files except the files set in the GLOBIGNORE variable:
$ rm -v *
removed 'file1 (3rd copy).jpg'
removed 'file1 (another copy).jpg'
removed 'file1 (copy).jpg'
removed 'file2 (3rd copy).png'
removed 'file2 (another copy).png'
removed 'file2 (copy).png'
removed 'file3 (3rd copy).pdf'
removed 'file3 (another copy).pdf'
removed 'file3 (copy).pdf'
removed 'file4 (3rd copy).zip'
removed 'file4 (another copy).zip'
removed 'file4 (copy).zip'
Most importantly, we can unset GLOBIGNORE:
$ unset GLOBIGNORE
It's good practice because GLOBIGNORE keeps the specified files excluded from pattern matching. In turn, when unaware of the exclusions, commands we run may not affect all files, which can be frustrating and hard to resolve.
However, when we set GLOBIGNORE, we use a Bash-specific feature. Therefore, pattern matching will automatically exclude . and .. from all matches.
5. Conclusion
In this article, we have covered three ways to delete files in a directory while keeping the files we still need.