Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

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 at 0:04

6 Answers 6

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:
    print(curdir)

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):
        print(curdir)

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 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 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 at 17:59
1  
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 at 18:15

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 at 18:41
    
I've given it another try. Badly –  Matthew Ife Jun 16 at 19:26

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

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 at 14:15
1  
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 at 14:19
    
This seems to work for me with plain a.ENDING filenames –  Iain Jun 16 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 at 14:30
3  
@bamboo I would give up with shell utilities and look for a different solution at this point. –  Iain Jun 16 at 14:31

I'd do it in perl personally

#!/usr/bin/perl

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

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
2  
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 at 18:34

Your Answer

 
discard

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.