Cristian Diaconescu
Cristian Diaconescu

Reputation: 35641

find filenames NOT ending in specific extensions on Unix?

Is there a simple way to recursively find all files in a directory hierarchy, that do not end in a list of extensions? E.g. all files that are not *.dll or *.exe

UNIX/GNU find, powerful as it is, doesn't seem to have an exclude mode (or I'm missing it), and I've always found it hard to use regular expressions to find things that don't match a particular expression.

I'm in a Windows environment (using the GnuWin32 port of most GNU tools), so I'm equally open for Windows-only solutions.

Upvotes: 291

Views: 221327

Answers (9)

Jonathan L
Jonathan L

Reputation: 10668

Use -not along with -regex

find . -type f -not -regex '.*\.\(exe\|dll\)'

Upvotes: 3

Vietnhi Phuvan
Vietnhi Phuvan

Reputation: 2804

Linux/OS X:

Starting from the current directory, recursively find all files ending in .dll or .exe

find . -type f | grep -P "\.dll$|\.exe$"

Starting from the current directory, recursively find all files that DON'T end in .dll or .exe

find . -type f | grep -vP "\.dll$|\.exe$"

Notes:

(1) The P option in grep indicates that we are using the Perl style to write our regular expressions to be used in conjunction with the grep command. For the purpose of excecuting the grep command in conjunction with regular expressions, I find that the Perl style is the most powerful style around.

(2) The v option in grep instructs the shell to exclude any file that satisfies the regular expression

(3) The $ character at the end of say ".dll$" is a delimiter control character that tells the shell that the filename string ends with ".dll"

Upvotes: 3

gwecho huang
gwecho huang

Reputation: 599

find  /data1/batch/source/export   -type f -not  -name "*.dll" -not -name "*.exe"

Upvotes: 3

VoteyDisciple
VoteyDisciple

Reputation: 37803

You could do something using the grep command:

find . | grep -v '(dll|exe)$'

The -v flag on grep specifically means "find things that don't match this expression."

Upvotes: 9

Chris Johnson
Chris Johnson

Reputation: 21956

Other solutions on this page aren't desirable if you have a long list of extensions -- maintaining a long sequence of -not -name 'this' -not -name 'that' -not -name 'other' would be tedious and error-prone -- or if the search is programmatic and the list of extensions is built at runtime.

For those situations, a solution that more clearly separates data (the list of extensions) and code (the parameters to find) may be desirable. Given a directory & file structure that looks like this:

.
└── a
    ├── 1.txt
    ├── 15.xml
    ├── 8.dll
    ├── b
    │   ├── 16.xml
    │   ├── 2.txt
    │   ├── 9.dll
    │   └── c
    │       ├── 10.dll
    │       ├── 17.xml
    │       └── 3.txt
    ├── d
    │   ├── 11.dll
    │   ├── 18.xml
    │   ├── 4.txt
    │   └── e
    │       ├── 12.dll
    │       ├── 19.xml
    │       └── 5.txt
    └── f
        ├── 13.dll
        ├── 20.xml
        ├── 6.txt
        └── g
            ├── 14.dll
            ├── 21.xml
            └── 7.txt

You can do something like this:

## data section, list undesired extensions here
declare -a _BADEXT=(xml dll)

## code section, this never changes
BADEXT="$( IFS="|" ; echo "${_BADEXT[*]}" | sed 's/|/\\|/g' )"
find . -type f ! -regex ".*\.\($BADEXT\)"

Which results in:

./a/1.txt
./a/b/2.txt
./a/b/c/3.txt
./a/d/4.txt
./a/d/e/5.txt
./a/f/6.txt
./a/f/g/7.txt

You can change the extensions list without changing the code block.

NOTE doesn't work with native OSX find - use gnu find instead.

Upvotes: 0

Hardy
Hardy

Reputation: 19119

Or without ( and the need to escape it:

find . -not -name "*.exe" -not -name "*.dll"

and to also exclude the listing of directories

find . -not -name "*.exe" -not -name "*.dll" -not -type d

or in positive logic ;-)

find . -not -name "*.exe" -not -name "*.dll" -type f

Upvotes: 458

logic
logic

Reputation: 21

one more :-)

$ ls -ltr
total 10
-rw-r--r--    1 scripter     linuxdumb         47 Dec 23 14:46 test1
-rw-r--r--    1 scripter     linuxdumb          0 Jan  4 23:40 test4
-rw-r--r--    1 scripter     linuxdumb          0 Jan  4 23:40 test3
-rw-r--r--    1 scripter     linuxdumb          0 Jan  4 23:40 test2
-rw-r--r--    1 scripter     linuxdumb          0 Jan  4 23:41 file5
-rw-r--r--    1 scripter     linuxdumb          0 Jan  4 23:41 file4
-rw-r--r--    1 scripter     linuxdumb          0 Jan  4 23:41 file3
-rw-r--r--    1 scripter     linuxdumb          0 Jan  4 23:41 file2
-rw-r--r--    1 scripter     linuxdumb          0 Jan  4 23:41 file1
$ find . -type f ! -name "*1" ! -name "*2" -print
./test3
./test4
./file3
./file4
./file5
$

Unix find command reference

Upvotes: 2

Jeff Ferland
Jeff Ferland

Reputation: 18292

$ find . -name \*.exe -o -name \*.dll -o -print

The first two -name options have no -print option, so they skipped. Everything else is printed.

Upvotes: 12

Chen Levy
Chen Levy

Reputation: 16368

find . ! \( -name "*.exe" -o -name "*.dll" \)

Upvotes: 53

Related Questions