Linux: Bash Delete All Files In Directory Except Few

I‘m a new Linux system user. I need to cleanup in a download directory i.e. delete all files from ~/Downloads/ folders except the following types:

*.iso – All iso images files.
*.zip – All zip files.

How do I delete all file except some in bash shell on a Linux, OS X or Unix-like systems?

Bash shell supports rich file pattern matching such as follows:[donotprint]

Tutorial details
Difficulty level Easy
Root privileges No
Requirements Bash
Est. reading time 2m
  • * - Match any files.
  • ? - Matches any single character in filenames.
  • [...] - Matches any one of the enclosed characters.

Method #1: Say hello to extended pattern matching operators

You need to use the extglob shell option using the shopt builtin command to use extended pattern matching operators such as:

  1. ?(pattern-list) - Matches zero or one occurrence of the given patterns.
  2. *(pattern-list) - Matches zero or more occurrences of the given patterns.
  3. +(pattern-list) - Matches one or more occurrences of the given patterns.
  4. @(pattern-list) - Matches one of the given patterns.
  5. !(pattern-list) - Matches anything except one of the given patterns.

A pattern-list is nothing but a list of one or more patterns (filename) separated by a |. First, turn on extglob option:

shopt -s extglob

Bash remove all files except *.zip and *.iso files

The rm command syntax is:

## Delete all file except file1 ##
rm  !(file1)
## Delete all file except file1 and file2 ##
rm  !(file1|file2) 
## Delete all file except all zip files ##
rm  !(*.zip)
## Delete all file except all zip and iso files ##
rm  !(*.zip|*.iso)
## You set full path too ##
rm /Users/vivek/!(*.zip|*.iso|*.mp3)
## Pass options ##
rm [options]  !(*.zip|*.iso)
rm -v  !(*.zip|*.iso)
rm -f  !(*.zip|*.iso)
rm -v -i  !(*.php)

Finally, turn off extglob option:

shopt -u extglob

Method #2: Using bash GLOBIGNORE variable to remove all files except specific ones

From the bash(1) page:

A colon-separated list of patterns defining the set of filenames to be ignored by pathname expansion. If a filename matched by a pathname expansion pattern also matches one of the patterns in GLOBIGNORE, it is removed from the list of matches.

To delete all files except zip and iso files, set GLOBIGNORE as follows:

## only works with BASH ##
cd ~/Downloads/
rm -v *

Method #3: Find command to rm all files except zip and iso files

If you are using tcsh/csh/sh/ksh or any other shell, try the following find command syntax on a Unix-like system to delete files:

find /dir/ -type f -not -name 'PATTERN' -delete


## deals with weird file names using xargs ##
find /dir/ -type f -not -name 'PATTERN' -print0 | xargs -0 -I {} rm {}
find /dir/ -type f -not -name 'PATTERN' -print0 | xargs -0 -I {} rm [options] {}

To delete all files except php files in ~/sources/ directory, type:

find ~/sources/ -type f -not -name '*.php' -delete


find ~/sources/ -type f -not -name '*.php' -print0 | xargs -0 -I {} rm -v {}

The syntax to delete all files except *.zip and *.iso is as follows:

find . -type f -not \( -name '*zip' -or -name '*iso' \) -delete

For more information see bash command man page and find command man page.

🐧 Get the latest tutorials on Linux, Open Source & DevOps via RSS feed or Weekly email newsletter.

🐧 17 comments so far... add one

CategoryList of Unix and Linux commands
Disk space analyzersdf ncdu pydf
File Managementcat tree
FirewallAlpine Awall CentOS 8 OpenSUSE RHEL 8 Ubuntu 16.04 Ubuntu 18.04 Ubuntu 20.04
Network UtilitiesNetHogs dig host ip nmap
OpenVPNCentOS 7 CentOS 8 Debian 10 Debian 8/9 Ubuntu 18.04 Ubuntu 20.04
Package Managerapk apt
Processes Managementbg chroot cron disown fg jobs killall kill pidof pstree pwdx time
Searchinggrep whereis which
User Informationgroups id lastcomm last lid/libuser-lid logname members users whoami who w
WireGuard VPNAlpine CentOS 8 Debian 10 Firewall Ubuntu 20.04
17 comments… add one
  • Jeff Jun 3, 2014 @ 21:16

    For your find command, instead of piping the results through xargs, you could also use the ‘-exec’ option, like so:

    find ~/sources/ -type f -not -name ‘*.php’ -exec rm {} \;

    Its just another option.

    • 🐧 Nix Craft Jun 4, 2014 @ 6:59

      Yup, thanks for the post :)

    • David Jun 9, 2014 @ 18:46

      It’s another option that orders of magnitude slower than xargs, because it calls one rm command per filename. There’s a much less well-known option than both of those as it handles weird filenames find and can execute a command with multiple filenames just as xargs does. Just replace the semicolon in the find command with a plus sign (which also eliminates the need for the ugly backslash):

      find ~/sources -type f -not -name ‘*.php’ -exec rm +

      Of course for rm, the -delete option is cleaner, but the -exec command + thing can be used with other commands too.

    • Starlyte Jul 24, 2014 @ 20:48

      I usually do MV to the Trash to be on the safe side, then rm the Trash file.
      I’ve had accidents doing a rapid rm.

      • Starlyte Jul 24, 2014 @ 20:49

        Well, in fact it’s mv, in miniscules.

  • Chris F.A. Johnson Jun 4, 2014 @ 0:49

    There is no need to export GLOBIGNORE.

    • 🐧 Nix Craft Jun 4, 2014 @ 6:59

      Ah, yes. Thanks for the heads up.

    • John Jun 4, 2014 @ 12:55

      Also, GLOBIGNORE works with other unix/bash/shell commands:
      ls *
      mv * /dest

      So you may need to unset GLOBIGNORE, as soon as you delete files :P

      • Chris F.A. Johnson Jun 10, 2014 @ 3:37

        GLOBIGNORE has nothing to do with those or any other commands; it affects how the shell expands wildcards, The commands (any commands) only see the result of the expansion.

  • Stephan Hughson Jun 4, 2014 @ 1:38

    Lazy option:

    Move the files you want to keep somewhere else.

    Delete everything that is left!

    Move the files you wanted to keep back again.

    It’s simple so little chance of making a mistake but the options above are pretty cool :-)

  • Scott Carlson Jun 4, 2014 @ 12:13

    I’ve never seen extglob before. Nice.

    I’d typically do something like…

    rm $(ls | grep -v -e iso$ -e zip$)

  • Mark Kenny Jun 4, 2014 @ 18:11

    Great script.

    Small error in very last code snippet, should be ‘*zip’ instead of ‘*php’

  • Deepak Dec 10, 2014 @ 10:21

    I am getting error on my environments. Is this command Linux OS dependent??

    $ rm -f !(|My.xml)
    -bash: !: event not found

  • Scott Carlson Dec 15, 2014 @ 1:40

    @Deepak, you forgot to turn on extglob first.

    shopt -s extglob
    ls !(|My.xml)

    • ikhmat Feb 11, 2015 @ 5:25

      What if we don’t turn extglob off after we started it?.. Will it cause any difference/problem for regular command that we run normally. So why Don’t we keep it “ON” all the time?. Sorry I am a starter:)))

  • Ashish Jun 3, 2015 @ 5:52

    what does ” -type f” do…because even without it, it does the same thing…can anyone explain please?

Leave a Reply

Your email address will not be published.

Use HTML <pre>...</pre> for code samples. Still have questions? Post it on our forum