Suchen Sie nach Dateien, die nicht mit der Liste der Dateinamenmuster übereinstimmen

7

Ich muss fremde Dateien finden und identifizieren (von ungefähr 900K-Dateien auf einem 2T-Laufwerk). Es gibt viele Dateien, die ich behalten möchte, und ich habe Dateinamenmuster für diese bekannten guten Dateien. Ich möchte die Dateien finden, die zu keinem der Muster passen.

Wie finde ich Dateien, die nicht mit einer Liste von Dateinamenmustern übereinstimmen?

Ich kann findeine Liste aller Dateien abrufen und grep -vfür das Ergebnis eine Liste der in einer Datei gespeicherten Muster verwenden. Ist dies die kanonische Methode oder haben Sie eine präzise Möglichkeit, diese nicht konformen Dateien zu finden?


Klarstellung - basierend auf den Antworten finden Sie hier einige weitere Informationen. Ich erwarte zahlreiche Muster (> 20, vielleicht> 100), möchte sie in einer Datei speichern und möchte auf jeden Fall auf einfache Weise neue hinzufügen. Ich würde es vorziehen, eine große Liste von Suchparametern (fragil) nicht direkt zu bearbeiten, aber das Erstellen dieser Liste könnte funktionieren.

ChuckCottrill
quelle
Bei der Perl-Antwort wird davon ausgegangen, dass Sie die Muster in einer separaten Datei gespeichert haben und daraus lesen. Es wird versucht, die Dateinamen entweder wörtlich oder durch Interpretation der Muster als Globs mit den Mustern abzugleichen.
Joseph R.
Ich habe meine Antwort bearbeitet, um die Anforderung "Muster in einer Datei" zu erfüllen
Warren Young

Antworten:

3

Da Sie Perl erwähnen ...

#!/usr/bin/perl

use strict;
use warnings;
use File::Find qw{find};

my %patterns;
while (<>) {
  chomp;
  $patterns{$_}++;
}

die "No pattern supplied\n" unless keys %patterns;

find( 
    sub{
           my $matches_a_pattern=0;
           for my $pattern (keys %patterns){
               my $glob_pattern = $pattern;
               for($glob_pattern){
                   s/\./\\./g;
                   s/\*/.*/g;
                   s/\?/./g;
               }
               $matches_a_pattern++ if ( /\Q$pattern\E/ or /$glob_pattern/);
           }

           print "$File::Find::name\n" unless $matches_a_pattern;
     }
    , '.' )

Rufen Sie dies als auf

/path/to/my/script file_with_patterns

Ersetzen Sie das .am Ende durch die Spitze des Baumes, den Sie gehen möchten.

Joseph R.
quelle
19

find(1)ist mächtig genug, um das zu tun, was Sie brauchen. Sammeln Sie einfach alle konformen Namen in Klammern in einem Ausdruck und negieren Sie ihn, um nicht konforme Dateinamen anzuzeigen. Zum Beispiel, um alle Dateien anzuzeigen, die nicht benannt *.txtsind *.bz2, oder *.zip:

$ find . \! \( -name \*.txt -o -name \*.bz2 -o -name \*.zip \)

Sie können -notanstelle von \!GNU und BSD verwenden find. Es ist nicht POSIX-kompatibel, erfordert jedoch kein Escape, um zu verhindern, dass die Shell es interpretiert.

Um den Ausdruck aus Mustern in einer Datei zu erstellen, ist es eine kleine Sache der Shell-Skripterstellung:

#!/bin/sh
set --
while IFS= read -r pattern
do
    set -- "$@" -o "$pattern"
done < .fnpatterns
if [ $# -ne 0 ]; then
  shift
  set -- -not \( "$@" \)
fi
find . "$@"

Dies erwartet eine Datei im aktuellen Verzeichnis, die .fnpatternsmit einem Muster pro Zeile aufgerufen wird. Um den obigen Einzeiler nachzuahmen, müsste er Folgendes enthalten:

*.txt
*.bz2
*.zip

Beachten Sie, dass das Shell-Skript die *Zeichen in den Mustern für Sie maskiert.

Sie können dies beliebig komplex machen. Einige Ideen:

  • In -type fmit dem findBefehl , so dass es nur normale Dateien zeigt, keine Verzeichnisse.

  • Übergeben Sie den Namen der Musterdatei als Argument, anstatt ihn an einem festen Ort zu erwarten

  • Behalten Sie die Musterdatei bei, wo sie sich befindet, aber fügen Sie -o -name .fnpatternssie dem erstellten findBefehl hinzu, damit sie nicht in der Ausgabe angezeigt wird. (Dies würde auch die Notwendigkeit vermeiden, dass der shiftHack die Führung -oim aufgebauten Ausdruck "frisst" .)

  • Fügen Sie dem findBefehl über -execoder ähnliches Aktionen hinzu .

  • Lassen Sie leere Zeilen oder Kommentare in der Musterdatei zu

Warren Young
quelle
was tut set --bedeuten?
Roberto
@Roberto: Die erste löscht der Positionsparameter des Skripts alle: $1, $2usw. Die zweite Appends -o $patternder Parameterliste, so dass wir alle am Ende der Schleife haben die Muster aus der .fnpatternsDatei , als ob an das Skript in quasi übergeben - findAusdrucksform. Wir haben ein Extra -oauf der Vorderseite, also schalten wir shiftes aus und wickeln das Ganze mit dem dritten set --Befehl in eine Negation . Jetzt enthält unsere Positionsparameterliste einen gültigen findAusdruck, den wir mit übergeben "$@".
Warren Young