Traversing filesystems [was Re: ASSP out of date]
Jordan K. Hubbard
jkh at apple.com
Sat Nov 8 16:25:13 PST 2008
[Adjusting topic to be a bit more descriptive of the current discussion]
On Nov 8, 2008, at 2:53 PM, Ryan Schmidt wrote:
> There is a function fs-traverse in MacPorts designed for this. Grep
> through the existing portfiles to see how this can be used.
As one of the authors of that function, I'd also be happy to take
"enhancement requests" for it since it was written to be pretty
generic and with only a couple of usage cases in mind at the time.
The code is very simple and not hard to extend, assuming the
enhancements make any kind of sense.
Regarding your earlier function, however, since I think we should
always help people learn Tcl where possible.. :-)
proc recursiveDirList { dir } {
set tmp_list {}
# Traverse top directory
set contents [glob -nocomplain -directory $dir *]
foreach item $contents {
lappend tmp_list $item
# Recurse - go into the sub directory
if { [file isdirectory $item] } {
lappend tmp_list [recursiveDirList $item]
}
}
# Final result, a list of all files and directories in $dir
return $tmp_list
}
You say: "First bad thing is that I do not want the directory in
there, there is no action I will ever take on a directory. Second,
when I print out the list, it is not a clean list, but a nested one,
and some items have "{" and "}" wrapping the path items, which makes
no sense to me. Stumped"
First, the directory items are an easy fix. Just don't lappend the
item unless it's a file, otherwise recurse. E.g.:
proc recursiveDirList { dir } {
set tmp_list {}
# Traverse top directory
set contents [glob -nocomplain -directory $dir *]
foreach item $contents {
# Recurse - go into the sub directory
if { [file isdirectory $item] } {
lappend tmp_list [recursiveDirList $item]
} else {
lappend tmp_list $item
}
}
# Final result, a list of all files and directories in $dir
return $tmp_list
}
That eliminates the extra directory entries. Now you have directories
expressed in their own lists, which is actually what you would
typically want and expect since this lets you pass the resulting
"uberlist" to some other function which walks through it and could
benefit from having the nesting level preserved, but OK, you want to
flatten everything and use absolute paths throughout, no problem.
Just change:
lappend tmp_list [recursiveDirList $item]
to
set tmp_list [concat $tmp_list [recursiveDirList $item]]
Now the lists are flattened. This brings us to your final point,
which is that some items have {}'s wrapping them. This is actually
not a bug, this is a feature, since Mac OS X filenames can contain
spaces and braces and other special characters that would confuse the
heck out of your subsequent pass through the list if they were not
"escaped" by the braces, e.g. "I am a file with a lot of spaces and
{crazy chars} in my name!" is going to cause you to look up each
element in the name otherwise. That bit of behavior you want to keep.
Finally, you say "In reality, it would be much more ideal to simply
supply a file extension list to the first function, and return a nice
clean list of the files"
I suspect you actually already know how to do this, you're just being
blinded by the obvious.. :) Final version:
proc recursiveDirList { dir pattern } {
set tmp_list {}
# Traverse top directory
set contents [glob -nocomplain -directory $dir *]
foreach item $contents {
# Recurse - go into the sub directory
if { [file isdirectory $item] } {
set tmp_list [concat $tmp_list [recursiveDirList $item
$pattern]]
} else {
if {[regexp $pattern $item]} { lappend tmp_list $item }
}
}
# Final result, a list of all files and directories in $dir
return $tmp_list
}
Voila. Now you can have, say, a list of all .c or .h files in a
directory by saying: recursiveDirList . {.*\.[ch]}
See, no one can say I'm not a believer in that ancient adage: "Give a
man a Tcl function and you have educated him for a day. Teach him
Tcl, on the other hand, and you have made an enemy for life!"
:-)
- Jordan
More information about the macports-users
mailing list