Server Fault is a question and answer site for system and network administrators. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I want to display all directories, that do not contain files with a specific file ending. Therefore I tried using the following code:

find . -type d \! -exec test -e '{}/*.ENDING' \; -print

In this example I wanted to display all directories, that do not contain files with the ending .ENDING, but this does not work.

Where is my mistake?

share|improve this question
This is not a duplicate of List directories not containing a file because that question doesn't deal with wildcards. – Cristian Ciupitu Jun 17 '14 at 0:04

Here's a solution in three steps:

temeraire:tmp jenny$ find . -type f -name \*ENDING -exec dirname {} \; |sort -u > /tmp/ending.lst
temeraire:tmp jenny$ find . -type d |sort -u > /tmp/dirs.lst
temeraire:tmp jenny$ comm -3 /tmp/dirs.lst /tmp/ending.lst 
share|improve this answer
One-liner without temporary files if using the Bash shell: comm -3 <(find . -type f -name \*ENDING -exec dirname {} \; |sort -u) <(find . -type d |sort -u) – Cristian Ciupitu Jun 16 '14 at 18:34

Here we go!

#!/usr/bin/env python3
import os

for curdir,dirnames,filenames in os.walk('.'):
  if len(tuple(filter(lambda x: x.endswith('.ENDING'), filenames))) == 0:

Or alternately (and more pythonic):

#!/usr/bin/env python3
import os

for curdir,dirnames,filenames in os.walk('.'):
    # Props to Cristian Cliupitu for the better python
    if not any(x.endswith('.ENDING') for x in filenames):

Bonus DLC Content!

The (mostly) corrected version of the find command:

find . -type d \! -exec bash -c 'set nullglob; test -f "{}"/*.ENDING' \; -print
share|improve this answer
I think the find solutions fails with test: ...: binary operator expected if there are multiple *.ENDING files in a directory. – Cristian Ciupitu Jun 16 '14 at 17:03
next(filter(lambda x: x.endswith('.ENDING'), filenames)) could also be written using generator comprehension i.e. next(x for x in filenames if x.endswith('.ENDING')). – Cristian Ciupitu Jun 16 '14 at 17:55
@CristianCiupitu: Truth: the find command is hacky and not really fully tested. Generator comprehension - yeah that's another nice way to do it. – MikeyB Jun 16 '14 at 17:59
I think an even nicer solution would be to use if not any(x.endswith('.ENDING') for x in filenames) based on the fact that any returns False for an empty iterable. – Cristian Ciupitu Jun 16 '14 at 18:15

The shell expands the *, but in your case there's no shell involved, just the test command executed by find. Hence the file whose existence is tested, is literally named *.ENDING.

Instead you should use something like this:

find . -type d \! -execdir sh -c 'test -e {}/*.ENDING' \; -print

This would result in sh expanding *.ENDING when test is executed.

Source: find globbing on UX.SE

share|improve this answer
Does not work. I get the following error: sh: -c: line 0: syntax error near unexpected token ('`. My directory names have the format 'xyz (dfdf)'. In fact its a calibre library. – bamboo Jun 16 '14 at 14:15
When I try it, I get sh: line 0: test: foo1/bar.ENDING: binary operator expected for the directory containing a file with the ending ENDING. – Jenny D Jun 16 '14 at 14:19
This seems to work for me with plain a.ENDING filenames – Iain Jun 16 '14 at 14:24
I have the following test directory structure: test->test (test)->test.ending After using this code I get sh: -c: line 0: syntax error near unexpected token (' sh: -c: line 0: 'test -e test (test)/*.ending' ./test (test)`. But when I change .ending to .xyz I get the same result. This is because i have bracts as directory name, right? How can I fix that? – bamboo Jun 16 '14 at 14:30
@bamboo I would give up with shell utilities and look for a different solution at this point. – Iain Jun 16 '14 at 14:31

Inspired by Dennis Nolte's and MikeyB's answers, I came up with this solution:

find . -type d                                                           \
  \! -exec bash -c 'shopt -s failglob; echo "{}"/*.ENDING >/dev/null' \; \
  -print 2>/dev/null

It works based on the fact that

if the failglob shell option is set, and no matches are found, an error message is printed and the command is not executed.

By the way, that's why stderr was redirected to /dev/null.

share|improve this answer

I'd do it in perl personally


use strict;
use warnings;

use File::Find;

#this sub is called by 'find' on each file it traverses. 
sub checkdir {
    #skip non directories. 
    return unless ( -d $File::Find::name );

    #glob will return an empty array if no files math - which evaluates as 'false'
    if ( not glob ( "$File::Find::name/*.ENDING" ) ) {
        print "$File::Find::name does not contain any files that end with '.ENDING'\n";

#kick off the search on '.' - set a directory path or list of directories to taste.
#  - you can specify multiple in a list if you so desire. 
find (  \&checkdir, "." );

Should do the trick (works on my very simplistic test case).

share|improve this answer

Heres a find one-liner.

find ./ -type d ! -regex '.*.ENDING$' -printf "%h\n" | sort -u

Edit: Oops, wont work either.

share|improve this answer
It doesn't work because you're testing the names of the directories, not of the files inside them. – Cristian Ciupitu Jun 16 '14 at 18:41
I've given it another try. Badly – Matthew Ife Jun 16 '14 at 19:26
find . -type d '!' -exec sh -c 'ls -1 "{}"|egrep -q "*ENDING$"' ';' -print
  • q in egrep is for quiet

  • With egrep you can swap the regex that you need

  • ls -1 "{}" outputs the file names from the find command

share|improve this answer

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.