vargrep

I was debugging someone else's code and was trying to track down where in a maze of namespaces a particular piece of data was stored. This prompted me to create this little proc which lets you search all known variables for a given string.

It's very simplistic, but solved my problem du jour.

Bryan Oakley 18-Oct-2001


 # finds variables whose value matches the given pattern. All variables 
 # in the given namespace and all children of the namespace are considered.
 #
 # pattern must be of the form expected by [string match]. Note that the 
 # pattern is fed directly to [string match]. So to find a string that 
 # contains the fragment foo you must specify a pattern of *foo*. Otherwise 
 # you'll only find variables that contain precisely "foo".
 #
 # This program returns a list. Each element is a string of the form 
 # "variablename: value". 

 proc vargrep {pattern {ns ::}} {

    set result {}
    # loop over all variables in the given namespace
    foreach var [info vars ${ns}::*] {

       if {[array exists $var]} {

          # this is an array; search through all elements
          foreach element [array names $var] {
             if {[string match $pattern [set ${var}($element)]]} {
                lappend result "${var}($element): [set ${var}($element)]"
             }
          }

       } else {
          # this is a scalar... search just the variable
          if {[string match $pattern [set $var]]} {
             lappend result "$var: [set $var]"
          }
       }
    }

    # now do the same for all children of this namespace
    foreach child [namespace children $ns] {
       set tmp [vargrep $pattern $child]
       set result [concat $result $tmp]
    }

    return $result
 }

JMN 2004-05-19 Note the above code breaks for variable declarations where no value has yet been assigned. e.g

 namespace eval test {
     variable blah
 }
 %vargrep something
 can't read "::test::blah": no such variable
 %array exists ::blah::hmm
 0
 %info exists ::blah::hmm
 0

I'd suggest wrapping the scalar if {[string match ... section in a simple catch. (rather than an info exists, as presumably such cases are the exception)