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
$find . -type f -not \( -name 'file1.jpg' -or -name 'file2.png' -or -name 'file3.pdf' -or -name 'file4.zip' \) -delete

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.

Comments are closed on this article!