rplantiko

List files with distinct content only

Jan 22nd, 2021 (edited)
1,196
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 4.72 KB | None | 0 0
  1. use strict;
  2. use warnings;
  3. use Digest::file qw(digest_file); # Für die MD5-Prüfsumme
  4.  
  5. # Liefert eine Liste aller csv-Files im angegebenen Verzeichnis (arg1),
  6. # deren Namen dem Suchmuster arg2 (regulärer Ausdruck) genügen.
  7. # Von inhaltsidentischen Files wird nur das neueste beibehalten
  8. # Zwei Files gelten als inhaltsidentisch, wenn
  9. # 1.) ihre MD5-Prüfsumme identisch ist, ODER
  10. # 2.) alle Zeilen in allen Zellwerten übereinstimmen mit Ausnahme der Spalten 1 und 5
  11. #     Diese Spalten enthalten Zeitstempel der Form JJJJMMTTHHMMSS,
  12. #     Die Files werden als identisch angesehen, selbst wenn diese Zeitstempel abweichen sollten
  13. # Es werden zwei anwendungsspezifische Gruppierungskriterien (Lieferantenname, Typ) verwendet,
  14. # sie stehen in der 2. und 3. Spalte der Fileinfos
  15. # In anderen Kontexten kann man stattedessen in sub get_file_info() andere Kriterien aufbauen.
  16.  
  17. # Directory und Filenamen-Pattern aus Kommandozeile einlesen
  18. if ($#ARGV < 1) {
  19.   die "Syntax: perl zafm_get_files.pl <Directory> <Filename regexp>\n";
  20. }
  21.  
  22. # Verzeichnis, ggf. schließenden Schrägstrich anfügen
  23. my $DIR     = ( $ARGV[0] =~ s/(?<!\/|\\)$/\//r );
  24. # Pattern als Filter für die Filenamen, z.B. (STAMM|BESTAND).*\.csv$
  25. my $PATTERN = qr/$ARGV[1]/i;
  26.  
  27. # Alle Dateien, die dem Pattern genügen, aus dem Verzeichnis lesen
  28. my @files = get_files( $DIR, $PATTERN );
  29.  
  30. # Dubletten ausdünnen (nur das jeweils neueste File stehenlassen)
  31. delete_equal( \@files );
  32.  
  33. # csv-Ergebnisliste ausgeben
  34. foreach my $file (@files) {
  35.   print join( ",", ( $file->[0], $file->[4], $file->[3]))."\n";
  36. }
  37.  
  38. # Dubletten aus der übergebenen Liste von Files entfernen
  39. sub delete_equal {
  40.   my ($files,$last,@files_new) = (shift);
  41.   @$files = sort { $a->[1] cmp $b->[1] ||    # Lieferantenname
  42.                    $a->[2] cmp $b->[2] ||    # Typ (STAMM oder BESTAND)
  43.                    $a->[4] <=> $b->[4] ||    # Dateigröße
  44.                    $a->[5] cmp $b->[5]       # MD5 Code
  45.                  } @$files;
  46.   foreach my $file (@$files) {
  47.     push @files_new, $last = $file unless
  48.       $last &&
  49.       $file->[1] eq $last->[1] &&
  50.       $file->[2] eq $last->[2] &&
  51.       $file->[4] == $last->[4] &&
  52.         ( $file->[5] eq $last->[5] ||
  53.           equal_content( $file, $last));    
  54.   }
  55. # Nach Dateinamen absteigend sortieren (=> pro Lief/Typ neuestes Datum zuerst)  
  56.   @$files = sort { $b->[0] cmp $a->[0] } @files_new;
  57. }
  58.  
  59. # Zwei Files auf Inhaltsgleichheit testen
  60. sub equal_content {
  61.  
  62.   my @files = @_[0,1];
  63.   my @filenames = map { $DIR.$_->[0] } @files;
  64.   my @fh = map { open_file( $_ ) } @filenames;
  65.  
  66.   my $equal = 1;
  67.   while (my $row0 = readline $fh[0]) {
  68.     my $row1 = readline $fh[1];
  69.     last unless $equal &&= (
  70.       $row1 &&
  71.       length($row0) == length($row1) &&
  72.       equal_rows( $row0, $row1 ));
  73.   }
  74.  
  75.   close $fh[0];
  76.   close $fh[1];
  77.   return $equal;
  78.  
  79. }
  80.  
  81. # Zwei csv-Zeilen auf Gleichheit prüfen
  82. sub equal_rows {
  83. # Den Zeitstempel am Anfang und in der 5. Spalte beim Vergleich ignorieren  
  84.   my ($row0,$row1) = map { s/
  85.     ^\d{14}         # 14 Ziffern am Anfang des Strings (KATALOGNR)
  86.     (
  87.       (?:;.*?){3}   # 3 Wiederholungen eines mit ; eingeleiteten Zellinhalts
  88.       ;             # Ein 4. Semikolon am Ende dieser Zellinhalte
  89.     )               # Diesen Abschnitt als $1 merken
  90.     \d{14}          # Wieder 14 Ziffern in Spalte 5 (ART_LIEFERBAR_AB)
  91.     /$1/rx          # Bei Substitution nur den Zwischenteil erhalten
  92.                     # Die beiden Zeitstempel werden somit gelöscht
  93.                     # Modifier 'r', um das Ergebnis der Substitution in 'map' zurückzugeben
  94.   } @_;
  95.   return $row0 eq $row1;
  96. }
  97.  
  98. # Einige Dateieigenschaften anreichern
  99. sub get_fileinfo {
  100.   my ($dirname,$filename) = @_;
  101.   my $file = $DIR.$filename;
  102.   my ($lief_name,$type) = ( $filename =~ /^(.*)_(STAMM|BESTAND)/ );
  103.   my @stat = stat $file;
  104.   return [
  105.       $filename,                   # 0: Filename (ohne DIR)
  106.       $lief_name,                  # 1: Lieferantenname
  107.       $type,                       # 2: STAMM | BESTAND
  108.       $stat[9],                    # 3: UX Timestamp: Geändert am
  109.       $stat[7],                    # 4: Size
  110.       digest_file($file,"MD5")     # 5: MD5-Prüfsumme
  111.     ]
  112. }
  113.  
  114. # Liefert alle Dateinamen eines Verzeichnisses, die einem Pattern genügen
  115. sub get_files {
  116.   my ($dirname,$pattern) = @_;
  117.   opendir my $dir, $dirname or die "Cannot open directory: $!";
  118.   my @filenames = grep { -f "$dirname/$_" && $_ =~ $pattern } readdir $dir;
  119.   my @files = map { get_fileinfo( $dirname,$_ ) } @filenames;
  120.   closedir $dir;
  121.   return @files;
  122. }
  123.  
  124. # Eine Datei zum Lesen öffnen
  125. sub open_file {
  126.   my $filename = shift;
  127.   open my $fh1, '<', $filename or die "Kann $filename nicht öffnen: $!";
  128.   return $fh1;
  129. }
  130.  
  131.  
Add Comment
Please, Sign In to add comment