TCL: Recursively search subdirectories to source all .tcl files

后端 未结 7 2067
独厮守ぢ
独厮守ぢ 2021-02-14 09:33

I have a main TCL proc that sources tons of other tcl procs in other folders and subsequent subdirectories. For example, in the main proc it has:

source $basepa         


        
相关标签:
7条回答
  • 2021-02-14 10:24

    It gets trivial with tcllib on board:

    package require fileutil
    foreach file [fileutil::findByPattern $basepath *.tcl] {
        source $file
    }
    
    0 讨论(0)
  • 2021-02-14 10:25

    Based on a previous answer, this version handles cycles created by symbolic links and in the process eliminates duplicate files due to symbolic links as well.

    # findFiles
    # basedir - the directory to start looking in
    # pattern - A pattern, as defined by the glob command, that the files must match
    proc findFiles {directory 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 directory [string trimright [file join [file normalize $directory] { }]]
    
        # Starting with the passed in directory, do a breadth first search for
        # subdirectories. Avoid cycles by normalizing all file paths and checking
        # for duplicates at each level.
    
        set directories [list]
        set parents $directory
        while {[llength $parents] > 0} {
    
            # Find all the children at the current level
            set children [list]
            foreach parent $parents {
                set children [concat $children [glob -nocomplain -type {d r} -path $parent *]]
            }
    
            # Normalize the children
            set length [llength $children]
            for {set i 0} {$i < $length} {incr i} {
                lset children $i [string trimright [file join [file normalize [lindex $children $i]] { }]]
            }
    
            # Make the list of children unique
            set children [lsort -unique $children]
    
            # Find the children that are not duplicates, use them for the next level
            set parents [list]
            foreach child $children {
                if {[lsearch -sorted $directories $child] == -1} {
                    lappend parents $child
                }
            }
    
            # Append the next level directories to the complete list
            set directories [lsort -unique [concat $directories $parents]]
        }
    
        # Get all the files in the passed in directory and all its subdirectories
        set result [list]
        foreach directory $directories {
            set result [concat $result [glob -nocomplain -type {f r} -path $directory -- $pattern]]
        }
    
        # Normalize the filenames
        set length [llength $result]
        for {set i 0} {$i < $length} {incr i} {
            lset result $i [file normalize [lindex $result $i]]
        }
    
        # Return only unique filenames
        return [lsort -unique $result]
    }
    
    0 讨论(0)
  • 2021-02-14 10:30

    The answer by Joseph Bui works well except that it skips files in the initial folder.

    Change:

    set directories [list]
    To:
    set directories [list $directory]

    to fix

    0 讨论(0)
  • 2021-02-14 10:30

    Building on ramanman's reply, heres a routine that tackles the problem using the built in TCL file commands and which works it way down the directory tree recursively.

    # 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
     }
    
    0 讨论(0)
  • 2021-02-14 10:31

    Perhaps a little more platform independent and using builtins commands instead of piping to a process:

    foreach script [glob [file join $basepath folderA *.tcl]] {
      source $script
    }
    

    Repeat for folderB.

    If you have more stringent selection criteria, and don't worry about running on any other platforms, using find may be more flexible.

    0 讨论(0)
  • 2021-02-14 10:34

    Here is one way:

    set includes [open "|find $basedir -name \*.tcl -print" r]
    
    while { [gets $includes include] >= 0 } {
      source $include
    }
    
    close $includes
    
    0 讨论(0)
提交回复
热议问题