fileutil_traverse(3tcl)
π fileutil_traverse β the file utilities manual, bestie edition β¨
okay so this is the fileutil_traverse(3tcl) page from the file utilities corner of the docs, and it's literally all about iterative directory traversal π¦. come sit with me, we're gonna walk through the whole thing together, no skips, no skimming π
πΈ NAME πΈ
fileutil_traverse β iterative directory traversal. that's it, that's the whole vibe. we go through directories, iteratively, one path at a time. periodt π
π SYNOPSIS π
package require Tcl 8.5 9
package require fileutil::traverse ?0.7?
package require fileutil
package require control
::fileutil::traverse ?objectName? path ?option value...?
$traverser command ?arg arg ...?
$traverser files
$traverser foreach filevar script
$traverser next filevar
yes, you need Tcl 8.5 9, and you need fileutil::traverse (version 0.7 is the optional bit), plus fileutil and control. get all of those and you're so good π«Ά
β¨ DESCRIPTION β¨
so this package hands you objects for programmable traversal of directory hierarchies. it's giving "i want to walk a whole tree and do stuff to every single thing in it." the main command the package exports is this one:
::fileutil::traverse ?objectName? path ?option value...?
- it creates a brand new traversal object, and that object gets a global Tcl command attached to it whose name is objectName. you use that command to do all the operations on the traverser. slay.
- if you pass the string
%AUTO%as the objectName, the package will generate a unique name for you all by itself. iconic, no naming stress π - for the options it recognizes, go peek at the OPTIONS section below. heads up though: all of those options can only be set when you create the traversal object. changing them afterwards? not possible. attempt it and errors get thrown at you. fr fr, no takebacks π
- the object command has this general form:
$traverser command ?arg arg ...?
the command plus its arguments decide exactly what the object does.
and these are the commands you can throw at a traversal object:
π $traverser files
this is the most high-level method the traversal objects give you. invoke it and it hands back a list containing the names of all the files and directories that match the traverser's current configuration. chef's kiss.
π $traverser foreach filevar script
the high-level files method above is actually built on top of this mid-level one. invoke it and it finds every file and directory matching the current configuration, then executes the script for each path. the path currently under consideration gets stored in the variable named by filevar. both the variable and the script live and get executed in the context of the caller of the method. and in the files method, the script is just quietly saving the found paths into the list it's gonna return. sneaky, but in a cute way π
π $traverser next filevar
this is the lowest possible interface to the traverser β the core that every higher method is built on. invoke it and it returns a boolean: True if it found a path matching the current configuration, False if it didn't. if it did find one, that path gets stored in the variable named by filevar, in the context of the caller.
the foreach method is literally just calling this in a loop until it comes back False. this method is exposed so you can also incrementally traverse a directory hierarchy in an event-based manner. main character energy for the event loop π
okay important note bestie: the traverser does follow symbolic links β except when following one would make it enter a link-cycle. in other words, it takes care not to lose itself in infinite loops when it bumps into circular link structures. and note that even links which are not followed will still show up in the result. π
π OPTIONS π
one bullet per option, all three of them, here we go:
-prefiltercommand_prefix β this callback runs for directories. its result decides whether the traverser recurses into that directory or not. default is to always recurse into all directories. the callback gets invoked with a single argument β the absolute path of the directory β and has to return a boolean value:Truewhen the directory passes the filter,Falseif not. vibe check for directories πͺ-filtercommand_prefix β this callback runs for all paths. its result decides whether the current path is a valid result and gets returned bynext. default is to accept all paths as valid. the callback gets invoked with a single argument β the absolute path to check β and has to return a boolean value:Truewhen the path passes the filter,Falseif not. β¨-errorcmdcommand_prefix β this callback runs for all paths the traverser has trouble with. like being unable to change into them, get their status, etc. default is to ignore any such problems. the callback gets invoked with two arguments: the absolute path for which the error occurred, and the error message. errors thrown by the filter callbacks get handled through this callback too. but errors thrown by the error callback itself are not caught and ignored β they're allowed to pass to the caller, i.e. whoever invoked thenext. any other results from the callback are ignored. π
π WARNINGS AND INCOMPATIBILITIES π
0.4.4 β in this version the traverser's broken system for handling symlinks got replaced with one that works correctly and properly enumerates all the legal non-cyclic paths under a base directory.
while that's correct, it also means certain pathological directory hierarchies with cross-linked sym-links will now take about O(n**2) time to enumerate, whereas the original broken code managed O(3tcl) due to its brokenness. yes bestie, that's what it says, i'm not touching it π
a concrete example and extreme case is the /sys hierarchy under Linux, where some hundred devices exist under both /sys/devices and /sys/class with the two sub-hierarchies linking to the other, generating millions of legal paths to enumerate. the structure, reduced to three devices, roughly looks like:
/sys/class/tty/tty0 --> ../../dev/tty0
/sys/class/tty/tty1 --> ../../dev/tty1
/sys/class/tty/tty2 --> ../../dev/tty1
/sys/dev/tty0/bus
/sys/dev/tty0/subsystem --> ../../class/tty
/sys/dev/tty1/bus
/sys/dev/tty1/subsystem --> ../../class/tty
/sys/dev/tty2/bus
/sys/dev/tty2/subsystem --> ../../class/tty
when you have to handle such a pathological hierarchy, it's recommended to use the -prefilter option to stop the traverser from following symbolic links. like so:
package require fileutil::traverse
proc NoLinks {fileName} {
if {[string equal [file type $fileName] link]} {
return 0
}
return 1
}
fileutil::traverse T /sys/devices -prefilter NoLinks
T foreach p {
puts $p
}
T destroy
the way that little NoLinks proc just says "nope, not a link, not today" and saves you from an infinite-ish crawl... we love a protective bestie π«Ά
π¦ BUGS, IDEAS, FEEDBACK π¦
this document, and the package it describes, will <highkey>undoubtedly</highkey> contain bugs and other problems. please report such in the category fileutil of the Tcllib Trackers at http://core.tcl.tk/tcllib/reportlist. please also report any ideas for enhancements you may have for either the package and/or the documentation. we want the glow-up, ngl π
when proposing code changes, please provide unified diffs, i.e. the output of diff -u.
note further that attachments are strongly preferred over inlined patches. attachments can be made by going to the Edit form of the ticket immediately after its creation, and then using the left-most button in the secondary navigation bar. iykyk π
π KEYWORDS π
directory traversal, traversal
π CATEGORY π
Programming tools
okay bestie that's the whole page, every single option and warning and example accounted for, nothing dropped, nothing invented π
and for the cute sign-off: this one comes to you from tcllib, version 0.7, page fileutil_traverse(3tcl). now go traverse something responsibly, and remember β prefilter your symlinks π§β¨π
π¦ where this page comes from
- debian trixie: tcllib 2.0+dfsg-4 Β· Tcl
- ubuntu questing: tcllib 2.0+dfsg-4
- ubuntu resolute: tcllib 2.0+dfsg-5 Β· Tcl
- ubuntu stonking: tcllib 2.0+dfsg-6 Β· Tcl
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms