Version 5 of dupfind

Updated 2006-07-19 14:15:31

A program to identify possible duplicates in the filesystem. For now the only criterias are (of course) the filename, the modification time and the size. Later on I will add a crc-check.

Example:

 dupfind d:/pgm/tcl/usr/tst > report.txt

A report (output) example:

 Startverzeichnis : D:/PGM/tcl/usr/Tst
 Suchmaske        : *
 Pruefungsdatum   : 19.07.2006-14:17:50

 Dateien gefunden : 5935

 Datei: .htaccess - Anzahl: 2 - Groesse: 171 - Zeit: 13.03.2001-07:57:20 - Ordner:

    D:/PGM/tcl/usr/Tst/httpd351pack/tclhttpd.vfs/htdocs/access/bydir
    D:/PGM/tcl/usr/Tst/WebSrv1/lib/tclhttpd3.4.3/htdocs/access/bydir

 Datei: .htaccess - Anzahl: 2 - Groesse: 223 - Zeit: 13.03.2001-07:17:52 - Ordner:

    D:/PGM/tcl/usr/Tst/httpd351pack/tclhttpd.vfs/htdocs/access/multiple
    D:/PGM/tcl/usr/Tst/WebSrv1/lib/tclhttpd3.4.3/htdocs/access/multiple

 Datei: .index - Anzahl: 2 - Groesse: 39 - Zeit: 05.05.2006-12:27:55 - Ordner:

    D:/PGM/tcl/usr/Tst/httpd342pack/daksite/wwwroot/wikihist/ccpm
    D:/PGM/tcl/usr/Tst/httpd351pack/daksite/wwwroot/wikihist/ccpm
 :
 :
 :



 Anzahl mehrfache : 933

 Bytes redundant  : 37423637


Script:

 ################################################################################
 #
 # Skript  : dupfind.tcl
 # Stand   : 19.07.2006
 # Status  : Entwurf, lauffähig
 # Autor   : M.Hoffmann
 # Sprache : Tcl
 # Zweck   : Erkennen mehrfach vorhandener Dateien anhand Name, Grösse, Zeit
 # Aufruf  : dupfind (startdir) [(suchmaske)|*]
 # Notizen : - CRC-Bildung sollte nach Eingrenzung durchgeführt werden
 #           - Dateigrössen sauber formatiert ausgeben (b,k,m,g) - der Code ist
 #             irgendwo schon vorhanden)
 #           - Optional verschiedene Ausgabeformate (Sort, Spalten etc.)
 #
 ################################################################################

 #*******************************************************************************
 # Direkt aus Modul eingefügt (und abgestrippt), damit sdx qwrap möglich
 #  schlecht: MITTLERER Parameter ist wahlfrei...
 #*******************************************************************************

 #****h* Library/globx.tcl
 #
 #  NAME
 #
 #     globx.tcl - Erweiterter Globbefehl (bearbeitet Verzeichnisbäume)
 #     v0.03, 09.12.2004
 #
 #  AUTHOR
 #
 #     M.Hoffmann, © 2004
 #
 #  PORTABILITY
 #
 #     Siehe TCL; getestet nur Win2000/XP
 #
 #  USAGE
 #
 #  -- package require globx
 #     ALLE ORDNER INKL. ALLEN SUBORDNERN UND DATEIEN, DIE SUCHMASKE ENTSPRECHEN:
 #  -- set files [globx startDir pattern]
 #     oder
 #     proc callback fileName {...}
 #  -- set numFiles [globx startDir pattern callback]
 #     ALLE ORDNER INKL. SUBORDNERN:
 #  -- set dirs [globx2 startDir]
 #     oder
 #     proc callback dirName {...}
 #  -- set numDirs [globx2 startDir callback]
 #
 #  NOTES
 #
 #  -- Nicht-rekursive Variante
 #  -- Bei Benutzung eines Callbacks kommen die Namen unsortiert herein
 #  -- Verzeichnisse werden immer ALLE durchsucht (*), Dateien JE NACH SUCHMASKE
 #  -- HIDDEN Entries müssen im Quelltext leider gesondert behandelt werden
 #  -- Tests mit globx_test.tcl, globx2_test.tcl
 #  -- ACHTUNG: Um die selben Ergebnisse wie unter DOS zu erreichen, muss *
 #     anstelle von *.* als Suchmaske angegeben werden!
 #
 #  TODO
 #
 #  -- Namespace
 #
 #  HISTORY
 #
 #     v0.01 06.02.2004 - Erste dokumentierte, einsatzfähige Version
 #     v0.02 21.10.2004 - Überarbeitung gemäss Wiki-Änderung-Empfehlung
 #     v0.03 09.12.2004 - Nur Ordnerbäume verarbeiten mit globx2
 #
 #  SOURCE

 proc globx {startDir {search *} {cb ""}} {
      set dirStack [list [file normalize $startDir]]
      set files {}
      set fc    0
      while {[llength $dirStack]} {
            set newStack {}
            foreach dir $dirStack {
                    # temporary var's only because eventually using CallBack
                    set fn [glob -noc -typ f          -dir $dir -- $search]
                    set fh [glob -noc -typ {f hidden} -dir $dir -- $search]
                    if {[string equal $cb ""]} {
                       eval lappend files $fn $fh
                    } else {
                       foreach f [concat $fn $fh] {
                               incr fc
                               uplevel [list $cb $f]
                       }
                    }
                    set dn [glob -noc -typ d          -dir $dir *]
                    set dh [glob -noc -typ {d hidden} -dir $dir *]
                    # eval lappend newStack $dn $dh; # v0.01
                    # Wikipatch Start v0.02 ---
                    foreach newDir [concat $dn $dh] {
                            set theDir [file tail $newDir]
                            if {[string equal $theDir "." ] || \
                                [string equal $theDir ".."]} {
                               # Don't push this, otherwise entering an endless
                               # loop (on UNIX, at least)
                            } else {
                               lappend newStack $newDir
                            }
                    }
                    # Wikipatch Ende ---
            }
            set dirStack $newStack
            update; # keep Background alive
      }
      if {[string equal $cb ""]} {
         return [lsort $files]
      } else {
         return $fc
      }
 }

 #*******************************************************************************

 proc callBack {ff} {

      global f
      global infos
      global v

      set fn [string tolower [string trim [file tail $ff]]]
      set fd [file dirname $ff]
      catch {
         # catch shouldn't be required here in theorie, but in practise it is...
         set size [file size $ff]
         set info [list $fn $size [file mtime $ff]]
         lappend f($info,dirs) $fd
         set dl [llength $f($info,dirs)]
         if {$dl == 2} {
            # merken, welche später auszugeben sind, aber nur 1x
            lappend infos $info
            puts -nonewline stderr .
            flush stderr
         }
         if {$dl > 1} {
            # verschwendete GesamtBytes hier mitzählen, spart Mult später...
            incr v $size
         }
      }; # show no erros from catch so far
 }

 #*******************************************************************************
 #*******************************************************************************
 #
 # MAIN
 #
 #*******************************************************************************
 #*******************************************************************************

 if {[llength $argv] < 1} {
    puts {
 dupfind 0.1 MHo

 Auffinden von mehrfach gespeicherten Dateien anhand Name, Datum, Modifikationszeit
 (Erweiterung durch CRC-Check und weitere Optionen sind geplant).

 Gebrauch: dupfind (startverzeichnis) [(suchmaske)|*] > ausgabe.datei

 ACHTUNG: *.* findet nur Dateien mit einer .Nameserweiterung!
 }
    exit 255;
 } elseif {[llength $argv] == 1} {
    lappend argv *
 }


 #*******************************************************************************
 # Globale Variablen
 #*******************************************************************************

 set infos {}
 array set f {}
 set v 0

 #*******************************************************************************

 puts "Startverzeichnis : [file normalize [lindex $argv 0]]"
 puts "Suchmaske        : [lindex $argv 1]"
 puts "Pruefungsdatum   : [clock format [clock seconds] -format %d.%m.%Y-%T]"
 puts {}

 set numFiles [globx [lindex $argv 0] [lindex $argv 1] callBack]

 puts stderr \n\n;
 puts "Dateien gefunden : $numFiles"
 puts {}

 if {$numFiles == 0} {
    exit 1;
 }

 #*******************************************************************************

 foreach info [lsort $infos] {
    puts "Datei: [lindex $info 0] - Anzahl: [llength $f($info,dirs)] -\
          Groesse: [lindex $info 1] -\
          Zeit: [clock format [lindex $info 2] -format %d.%m.%Y-%T] - Ordner:\n"
    foreach dir $f($info,dirs) {
       puts "   $dir"
    }
    puts {};
 }

 puts "\nAnzahl mehrfache : [llength $infos]"
 puts "\nBytes redundant  : $v\n"

 exit 0;

To compile, use sdx qwrap -runtime tclkitsh.exe


More infos follow later ;-)