TCL:Рекурсивный поиск подкаталогов для получения всех исходных файлов .tcl

StackOverflow https://stackoverflow.com/questions/429386

  •  06-07-2019
  •  | 
  •  

Вопрос

У меня есть основной TCL-процесс, который создает тонны других tcl-процессов в других папках и последующих подкаталогах.Например, в основном процессе он имеет:

source $basepath/folderA/1A.tcl
source $basepath/folderA/2A.tcl
source $basepath/folderA/3A.tcl
source $basepath/folderB/1B.tcl
source $basepath/folderB/2B.tcl
source $basepath/folderB/3B.tcl

и кажется немного глупым делать это таким образом, когда я всегда знаю, что у меня будут исходные тексты для всего в FolderA и FolderB.Есть ли функция (или простой способ), которая позволит мне просто создать все файлы .tcl во всей папке?

Это было полезно?

Решение

Основываясь на ответе Раманмана, он представляет собой подпрограмму, которая решает проблему с помощью встроенных команд файла TCL и рекурсивно обрабатывает ее по дереву каталогов.

# 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
 }

Другие советы

С tcllib на борту это становится тривиально:

package require fileutil
foreach file [fileutil::findByPattern $basepath *.tcl] {
    source $file
}

Возможно, немного более независимый от платформы и использующий встроенные команды вместо передачи в процесс:

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

Повторите для папки B.

Если у вас более строгие критерии выбора и вы не беспокоитесь о работе на других платформах, использование find может быть более гибким.

Основываясь на предыдущем ответе, эта версия обрабатывает циклы, созданные символическими ссылками, и в процессе удаляет дубликаты файлов также благодаря символическим ссылкам.

# 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]
}

Та же идея, что и у Шленка:

package require Tclx
for_recursive_glob scriptName $basepath *.tcl {
    source $scriptName
}

Если вам нужны только папка A и папка B, а не другие папки в $ basepath:

package require Tclx
for_recursive_glob scriptName [list $basepath/folderA $basepath/folderB] *.tcl {
    source $scriptName
}

Вот один из способов:

set includes [open "|find $basedir -name \*.tcl -print" r]

while { [gets $includes include] >= 0 } {
  source $include
}

close $includes

Ответ Джозефа Буя работает хорошо, за исключением того, что он пропускает файлы в исходной папке.

Изменение:

set directories [list]
Для:
set directories [list $directory]

чтобы исправить

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top