4

Using an old answer to search for a file in tcl: https://stackoverflow.com/a/435094/984975

First lets discuss what I am doing right now: Using this function:(credit to Jacson)

# findFiles
# basedir - the directory to start looking in
# pattern - A pattern, as defined by the glob command, that the files must match
proc findFiles { basedir pattern } {

    # Fix the directory name, this ensures the directory name is in the
    # native format for the platform and contains a final directory seperator
    set basedir [string trimright [file join [file normalize $basedir] { }]]
    set fileList {}

    # Look in the current directory for matching files, -type {f r}
    # means ony readable normal files are looked at, -nocomplain stops
    # an error being thrown if the returned list is empty
    foreach fileName [glob -nocomplain -type {f r} -path $basedir $pattern] {
        lappend fileList $fileName
    }

    # Now look for any sub direcories in the current directory
    foreach dirName [glob -nocomplain -type {d  r} -path $basedir *] {
        # Recusively call the routine on the sub directory and append any
        # new files to the results
        set subDirList [findFiles $dirName $pattern]
        if { [llength $subDirList] > 0 } {
            foreach subDirFile $subDirList {
                lappend fileList $subDirFile
            }
        }
    }
    return $fileList
 }

And calling the following command:

findFiles some_dir_name *.c

The current result:

bad option "normalize": must be atime, attributes, channels, copy, delete, dirname, executable, exists, extension, isdirectory, isfile, join, lstat, mtime, mkdir, nativename, owned, pathtype, readable, readlink, rename, rootname, size, split, stat, tail, type, volumes, or writable

Now, if we run:

glob *.c

We get a lot of files but all of them in the current directory.

The goal is to get ALL the files in ALL sub-folders on the machine with their paths. Anyone who could help?

What I really want to do is find the directory with the highest count of *.c files. However, if I could list all the files and their paths, I could count, how many files are in each directory and get the one with the highest count.

Community
  • 1
  • 1
AturSams
  • 7,568
  • 18
  • 64
  • 98
  • The error comes from an too old Tcl version, try using a more recent Tcl, you need at least Tcl 8.4 (which is only 10 years old...). – schlenk Jun 19 '12 at 19:13
  • If I make my directory names with spaces at the end (which is legal on most systems, but very tricky!) that code is totally doomed! Bwahahaha! (Don't `string trim` things that come out of `glob`…) – Donal Fellows Jun 20 '12 at 09:36

3 Answers3

4

You are using an old version of Tcl. [file normalize] was introduced in Tcl 8.4 around 2002 or so. Upgrade already.

If you can't - then you use glob but call it once just for files and then walk the directories. See the glob -types option.

Here's a demo:

proc on_visit {path} {
    puts $path
}

proc visit {base glob func} {
    foreach f [glob -nocomplain -types f -directory $base $glob] {
        if {[catch {eval $func [list [file join $base $f]]} err]} {
            puts stderr "error: $err"
        }
    }
    foreach d [glob -nocomplain -types d -directory $base *] {
        visit [file join $base $d] $glob $func
    }
}

proc main {base} {
    visit $base *.c [list on_visit]
}

main [lindex $argv 0]
patthoyts
  • 32,320
  • 3
  • 62
  • 93
4

I would use the ::fileutil::traverse function to do it.

Something like:

package require ::fileutil::traverse

proc check_path {path} {
     string equal [file extension $path] ".c"
}

set obj [::fileutil::traverse %AUTO% -filter check_path]
array set pathes {}
$obj foreach file {
     if {[info exists pathes([file dirname $file])]} {
        incr pathes([file dirname $file])
     } else {
        set pathes([file dirname $file]) 1
     }
}

# print pathes and find the biggest
foreach {name value} [array get pathes] {
     puts "$name : $value"
}
schlenk
  • 7,002
  • 1
  • 25
  • 29
  • this one requires that tcllib is installed in addition to tcl. sudo apt-get install tcllib tcl tk – Bimo Jun 05 '20 at 23:42
2

For quick (1 level) file pattern matching use:

glob **/*.c

If you want to recursively search, then use:

proc ::findFiles { baseDir pattern } {
  set dirs [ glob -nocomplain -type d [ file join $baseDir * ] ]
  set files {}
  foreach dir $dirs { 
    lappend files {*}[ findFiles $dir $pattern ] 
  }
  lappend files {*}[ glob -nocomplain -type f [ file join $baseDir $pattern ] ] 
  return $files
}

puts [ join [ findFiles $basepath "*.tcl" ] \n ]
Sooth
  • 2,834
  • 23
  • 26