2009-01-09 68 views
10

Tôi có một TCL proc chính cung cấp nhiều tấn procs tcl khác trong các thư mục khác và các thư mục con tiếp theo. Ví dụ: trong proc chính, nó có:TCL: Tìm kiếm đệ quy các thư mục con để nguồn tất cả các tệp .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 

và có vẻ như ngu ngốc khi làm như vậy khi tôi luôn biết mình sẽ nguồn mọi thứ trong thư mụcA và folderB. Có một chức năng (hoặc cách đơn giản) mà sẽ cho phép tôi chỉ cần nguồn tất cả các tập tin .tcl trong một thư mục toàn bộ?

Trả lời

10

Xây dựng dựa trên câu trả lời của người ramanman, đó là một thói quen giải quyết vấn đề bằng cách sử dụng các lệnh tệp TCL được xây dựng và hoạt động theo cách đệ quy xuống cây thư mục.

# 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

Cảm ơn Jackson. Tôi nghĩ rằng chúng ta có thể đặt tất cả điều này để nghỉ ngơi ngay bây giờ! – Lyndon

+3

Nếu bạn có liên kết tượng trưng tạo chu trình, bạn sẽ nhận được "quá nhiều đánh giá lồng nhau (vòng lặp vô hạn?)". –

1

Dưới đây là một cách: lệnh

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

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

close $includes 
+0

Cảm ơn. Điều này làm việc hoàn hảo. – Lyndon

5

Có lẽ một chút nền tảng hơn builtins độc lập và sử dụng thay vì đường ống để một quá trình:

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

Lặp lại cho folderB.

Nếu bạn có tiêu chí lựa chọn nghiêm ngặt hơn và đừng lo lắng về việc chạy trên bất kỳ nền tảng nào khác, việc tìm kiếm có thể linh hoạt hơn.

+0

Điều duy nhất tôi nhận thấy là điều này trả về lỗi nếu không có tệp nào phù hợp, nhưng phải thừa nhận rằng tôi không kiểm tra câu trả lời khác. – Lyndon

+0

sử dụng tùy chọn -nocomplain trên lệnh glob để ngăn chặn nó ném và lỗi nếu một danh sách trống được tạo ra. – Jackson

2

Dựa trên câu trả lời trước, phiên bản này xử lý các chu kỳ được tạo bởi liên kết tượng trưng và trong quá trình loại bỏ các tệp trùng lặp do liên kết tượng trưng.

# 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

Nó được tầm thường với tcllib trên tàu:

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

Cùng ý tưởng như SCHLENK:

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

Nếu bạn chỉ muốn folderA và folderB và không thư mục khác dưới $ basepath:

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

Câu trả lời của Joseph Bui hoạt động tốt ngoại trừ việc nó bỏ qua các tập tin trong thư mục ban đầu.

Thay đổi:

set directories [list]
Để:
set directories [list $directory]

để sửa chữa

Các vấn đề liên quan