Suchen Sie nach Verzeichnissen, in denen Dateien mit einer bestimmten Endung fehlen

10

Ich möchte alle Verzeichnisse anzeigen, die keine Dateien mit einer bestimmten Dateiende enthalten. Deshalb habe ich versucht, den folgenden Code zu verwenden:

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

In diesem Beispiel wollte ich alle Verzeichnisse anzeigen, die keine Dateien mit der Endung enthalten .ENDING, aber das funktioniert nicht.

Wo ist mein Fehler?

Bambus
quelle
Dies ist kein Duplikat von Listenverzeichnissen, die keine Datei enthalten, da diese Frage keine Platzhalter behandelt.
Cristian Ciupitu

Antworten:

7

Hier ist eine Lösung in drei Schritten:

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 
Jenny D.
quelle
2
comm -3 <(find . -type f -name \*ENDING -exec dirname {} \; |sort -u) <(find . -type d |sort -u)
Einzeiler
4

Auf geht's!

#!/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)

Oder alternativ (und pythonischer):

#!/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 Inhalt!

Die (meistens) korrigierte Version des Befehls find:

find . -type d \! -exec bash -c 'set nullglob; test -f "{}"/*.ENDING' \; -print
MikeyB
quelle
Ich denke, die Suchlösungen schlagen fehl, test: ...: binary operator expectedwenn sich mehrere *.ENDINGDateien in einem Verzeichnis befinden.
Cristian Ciupitu
next(filter(lambda x: x.endswith('.ENDING'), filenames))könnte auch unter Verwendung des Generatorverständnisses geschrieben werden, dh next(x for x in filenames if x.endswith('.ENDING')).
Cristian Ciupitu
@CristianCiupitu: Wahrheit: Der Befehl find ist hacky und nicht wirklich vollständig getestet. Generatorverständnis - ja, das ist ein weiterer guter Weg, dies zu tun.
MikeyB
1
Ich denke, eine noch schönere Lösung wäre es, if not any(x.endswith('.ENDING') for x in filenames)basierend auf der Tatsache zu verwenden , dass alle Rückgaben Falsefür eine leere iterable.
Cristian Ciupitu
3

Die Hülle dehnt sich das *, aber in Ihrem Fall gibt es keine Shell beteiligt sind , nur der Test - Befehl ausgeführt finden . Daher wird die Datei, deren Existenz getestet wird, buchstäblich benannt *.ENDING.

Stattdessen sollten Sie Folgendes verwenden:

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

Dies würde dazu führen, dass sh erweitert wird, *.ENDINGwenn der Test ausgeführt wird.

Quelle: Finden Sie Globbing auf UX.SE.

Dennis Nolte
quelle
Funktioniert nicht. Ich erhalte die folgende Fehlermeldung: sh: -c: line 0: syntax error near unexpected token ('`. Meine Verzeichnisnamen haben das Format' xyz (dfdf) '. Tatsächlich handelt es sich um eine Kaliberbibliothek.
Bambus
1
Wenn ich es versuche, erhalte ich sh: line 0: test: foo1/bar.ENDING: binary operator expecteddas Verzeichnis, das eine Datei mit der Endung enthält ENDING.
Jenny D
Dies scheint für mich mit einfachen a.ENDING-Dateinamen zu
funktionieren
Ich habe die folgende Testverzeichnisstruktur: test-> test (test) -> test.ending Nach Verwendung dieses Codes erhalte ich sh: -c: line 0: syntax error near unexpected token ('sh: -c: Zeile 0:' test -e test (test) / *. Ending '). / test (test) `. Aber wenn ich .ending auf .xyz ändere, erhalte ich das gleiche Ergebnis. Das liegt daran, dass ich Deckblätter als Verzeichnisnamen habe, richtig? Wie kann ich das beheben?
Bambus
3
@bamboo Ich würde mit Shell-Dienstprogrammen aufgeben und an dieser Stelle nach einer anderen Lösung suchen.
user9517
2

Inspiriert von den Antworten von Dennis Nolte und MikeyB habe ich diese Lösung gefunden:

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

Es funktioniert basierend auf der Tatsache, dass

Wenn die Failglob- Shell-Option festgelegt ist und keine Übereinstimmungen gefunden werden, wird eine Fehlermeldung ausgegeben und der Befehl nicht ausgeführt.

Das ist übrigens der Grund, warum stderr umgeleitet wurde /dev/null.

Cristian Ciupitu
quelle
1

Ich würde es persönlich in Perl machen

#!/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, "." );

Sollte den Trick machen (funktioniert bei meinem sehr simplen Testfall).

Sobrique
quelle
0

Hier ist ein Einzeiler.

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

Edit : Ups, wird auch nicht funktionieren.

Matthew Ife
quelle
Es funktioniert nicht, weil Sie die Namen der Verzeichnisse testen, nicht der darin enthaltenen Dateien.
Cristian Ciupitu
Ich habe es noch einmal versucht. Schlecht
Matthew Ife
0
find . -type d '!' -exec sh -c 'ls -1 "{}"|egrep -q "*ENDING$"' ';' -print
  • q in egrep ist für leise

  • Mit können egrepSie den Regex tauschen, den Sie benötigen

  • ls -1 "{}" gibt die Dateinamen aus dem Befehl find aus

sk8asd123
quelle