2009-01-09 9 views
10

J'ai un proc TCL principal qui trouve des tonnes d'autres proc tcl dans d'autres dossiers et sous-répertoires subséquents. Par exemple, dans le principal proc il a:TCL: Recherche récursive des sous-répertoires pour la source de tous les fichiers .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 

et il semble un peu stupide de le faire de cette façon quand je sais toujours que je référencerons tout dossierA et FolderB. Existe-t-il une fonction (ou une méthode simple) qui me permettra de simplement stocker tous les fichiers .tcl dans un dossier entier?

Répondre

10

S'appuyant sur la réponse de Ramanman, voici une routine qui résout le problème en utilisant les commandes du fichier TCL intégré et qui descend dans l'arborescence de répertoires de manière récursive.

# 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

Merci Jackson. Je pense que nous pouvons tout mettre au repos maintenant! – Lyndon

+3

Si vous avez un lien symbolique qui crée un cycle, vous obtiendrez l'erreur "trop ​​d'évaluations imbriquées (boucle infinie?)". –

1

Voici une façon:

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

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

close $includes 
+0

Merci. Cela a fonctionné parfaitement. – Lyndon

5

Peut-être un peu plus la plate-forme des commandes indépendantes et à l'aide builtins au lieu de la tuyauterie à un processus:

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

Répétez l'opération pour FolderB.

Si vous avez des critères de sélection plus stricts et que vous ne vous souciez pas d'utiliser d'autres plates-formes, l'utilisation de find peut être plus flexible.

+0

La seule chose que j'ai remarqué est que cela renvoie une erreur si aucun fichier ne correspond, mais je n'ai pas vérifié ce que l'autre réponse a fait. – Lyndon

+0

utilisez l'option -nocomplain de la commande glob pour l'arrêter et lancer une erreur si une liste vide est générée. – Jackson

2

Sur la base d'une réponse précédente, cette version gère les cycles créés par des liens symboliques et dans le processus élimine les fichiers en double en raison de liens symboliques.

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

Il obtient trivial avec tcllib à bord:

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

idée même que Schlenk:

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

Si vous ne souhaitez que Foldera et FolderB et pas d'autres dossiers sous basePath de $:

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

La réponse de Joseph Bui fonctionne bien, sauf qu'elle ignore les fichiers dans le dossier initial.

Changement:

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

pour fixer

Questions connexes