Windows file finder

As a result of the recent postings on clt, concerning a tcl/tk only file finder for windows, the following is a crude, slow, but working script (which really has a lot of room for improvement), however, it does work;

 # A little piece of work due to recent discussions on clt
 # drives.tcl from the tclers Wiki
 # find from Jeffery Hobbs, from the tcllib, from the Wiki
 # pp interface etc by Steve Offutt Thursday January 11, 2001

 #mapped drives: 
 #from the tcler's Wiki

  proc drives {} {
    foreach drive \
     [list a b c d e f g h i j k l m n o p q r s t u v w x y z] {
         if {[catch {file stat ${drive}: dummy}] == 0} {
             lappend drives $drive
    return $drives

 #source drives.tcl
 set mylist [drives]

 #just in case we need to know how many
 set count [llength $mylist]
 #which it turns out we dont - yet

 global file_count
 set file_count "0"

 set current [pwd]
 set drv_ltr  [string index $current 0]
 set drive "$drv_ltr:/"

 proc make_rb { list parent} {
        global drv_ltr
        foreach item $list {
        grid [radiobutton $parent.$item -text [string toupper "$item:"] -variable drv_ltr \
                -command {chg_drv} -value [string toupper "$item" ] ] 


 proc chg_drv { } {
        global drive
        global drv_ltr
        set drive "$drv_ltr:/"

 menu .menubar -type menubar
 .menubar add cascade -label "File" -menu .menubar.file -underline 0
 .menubar add cascade -label "New Search" -menu -underline 0

 menu .menubar.file -tearoff 0
 .menubar.file add command -label Exit -underline 1 -command { exit}

 menu -tearoff 0 add command -label Clear -underline 0 -command { clear }

 . configure -menu .menubar

 frame .main -bd 1 -relief groove -width 300 -height 300
 frame -bd 2 -relief groove -width 300 -height 150
 frame -bd 2 -relief flat -width 150 -height 150 
 frame -bd 2 -relief flat -width 150 -height 150 
 listbox -yscrollcommand " set" -selectmode browse \
                -xscrollcommand " set" \
                -bg white -width 30
 scrollbar -command " yview"
 scrollbar -command " xview" -orient horizontal
 label -text "Drives:"

 label -text "File to find:" 
 entry -textvariable find_this -width 30
 label -text "Current directory:"
 label -text [pwd]
 label -text "Drive to search:"
 entry -textvariable drive 
 button -text "Search now" -command {search_now} -relief groove
 label -relief flat -textvariable file_count

 grid -sticky ew
 grid -sticky ew
 grid -sticky ew
 grid -sticky ew -columnspan 1
 grid -sticky ew -column 0 -columnspan 2

 grid -sticky ew -columnspan 1
 make_rb $mylist
 grid -sticky news -columnspan 1 -column 0
 label -text "Matching files:"
 grid -sticky ew
 grid -sticky nsew
 grid -sticky snew

 grid -sticky news -row 1 -column 1 -columnspan 1
 grid .main -columnspan 2

 global my_file_list
 set my_file_list { }

 namespace eval ::fileutil {}
 proc ::fileutil::find {{basedir .} {filtercmd {}}} {
        #another change
        global files
     set oldwd [pwd]
     cd $basedir
     set cwd [pwd]
     set filenames [glob -nocomplain * .*]
     set files {}
     set filt [string length $filtercmd]
     # If we don't remove . and .. from the file list, we'll get stuck in an infinite loop 
 foreach special [list "." ".."] {
 set index [lsearch -exact $filenames $special]
 set filenames [lreplace $filenames $index $index]
     foreach filename $filenames {
 # Use uplevel to eval the command, not eval, so that variable 
 # substitutions occur in the right context.
 if {!$filt || [uplevel $filtercmd [list $filename]]} {
     lappend files [file join $cwd $filename]
 if {[file isdirectory $filename]} {
     set files [concat $files [find $filename $filtercmd]]
     cd $oldwd
     return $files

  # Use like:
 #::fileutil::find $dir {string equal README}

 proc search_now { } {
        global find_this
        global my_file_list
        global drive
        global file_count
        set dir $drive
        set my_string "string equal -nocase $find_this"
        set my_file_list [::fileutil::find $dir $my_string]
        set file_count [llength $my_file_list]

 bind <Return> {search_now}

 proc show_list { } {
        global my_file_list
        foreach item $my_file_list { insert end $item

 proc clear { } { delete 0 end delete 0 end

 console hide
 wm title . "Tk File Finder (windoze)"
 wm deiconify .

The one thing that you can do with this is to build lists of files with varying filenames across different drives. You will have to script a way to export the list for yourself... ;^)


In order to properly search an NTFS system (where a user might not have permissions on all directories, change the following: In

 proc ::fileutil::find {{basedir .} {filtercmd {}}} {

# assorted commands removed

     if {![catch [cd $basedir]]} {

# assorted commands removed

     cd $oldwd
     return $files

Ryan Casey

LES Of course it is slow. You'll be a lot better off building and saving a file name list that can be searched later on instead of scanning disks in every search. In my system, whenever PowerPro detects lack of mouse and keyboard activity for 30 minutes, it launches my Tcl script to scan all drives and update the list. When I run a search, I'm only searching a flat text-based list, so it is fast. It would be even faster with an SQLite database (although the first query within a certain time span always is slow).

And I almost forgot to add an interesting bit: my method follows exactly the same model of the updatedb/slocate pair of Unix tools, but in my experience, Tcl always seems to scan and index my disks considerably faster than updatedb.

Another way of recursively file walking is shown on page Matthias Hoffmann - Tcl-Code-Snippets.