Find directories, where files with a specific ending are missing
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?
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
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
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
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
.
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).