0

In my script, I will source other owner's TCL script.

I try to check all variable if redefined in their tcl script file, Here I try in my script:

proc trace_var {args} {
   set frame [info frame];
   set frame_info [info frame [expr {$frame -2}]];
   puts "$args set in $frame_info"
}
trace add execution set leave trace_var
#source other owner script file
source #FILE1_FROM_OTHERS
#file content:
set a 123
set b 123456 ..... 
#Just example, I don't know the all var name, in fact. 
source #FILE2_FROM_OTHERS
set xxx 456;
set yyy 999;
set a 2; #<- want to catch such case
#Do my remain script body

Update: There are more than 3000+ vars in #File1, #File2, #FileN.. I have no clue how to use by tracing variable. The case I want to catch is if any variable redefine in #FILE1, #FILE2, ... #FILEN

2
  • Why not trace the variable instead? There are other ways to write to a variable than set... Commented Oct 14, 2023 at 16:09
  • the varaible is source from other people file, there are over 3000+ variables in file, I don't know the var name. Commented Oct 15, 2023 at 2:06

1 Answer 1

2

There are many ways a variable can be created or changed other than using the set command, really too many to be feasible to attempt to monitor like this. Just some examples:

lassign $foo a b     ;# variables a and b set / updated
dict set foo bar baz ;# variable foo set or updated
foreach element $list {}   ;# variable element set
dict with foo {}     ;# any keys in $foo become variables

And this is not limited to Tcl built-in commands, for example my json command has equivalents to most of the above examples, so unless you prevent new commands being defined by the script you're sourcing it's not even possible to track it by tracing all the known commands that could affect variables.

I'm not sure about the details of your use case, but if it's just global variables I might try something like this:

proc var_changed {v n1 n2 op} {
    puts "variable $v $op"
}

set start_vars [info vars]
foreach v $start_vars {
    trace add variable $v {write unset} [list var_changed $v]
}

source $other_file

foreach v [info vars] {
    if {$v ni $start_vars} {
        puts "New var defined: $v"
    }
}

While the trace handler is executing, additional traces on the variable that triggered it are suppressed.

This example doesn't handle all the cases that could happen with array variables, or variables that are unset and then later added back. It's possible to extend this technique to variables in namespaces, but it starts to feel fragile and heavy-handed. If your use case is really about containing side-effects by the sourced script with unknown contents, then I'd advise rather sourcing the script in a child interpreter (a safe child if you don't trust the source of the script), maybe like this:

set child [interp create -safe]
$child eval [list set foo $foo]    ;# Copy the state of selected variables
$child eval [list set bar $bar]    ;# to the child interp

$child eval [list source $other_file]

# Retrieve the state of vars in the child after sourcing $other_file if needed:
puts "foo in child: [$child eval [list set foo]]"

# Clean up
interp delete $child
Sign up to request clarification or add additional context in comments.

2 Comments

It work, but can I know first variable define in which file, line? even I don't know the var name
With either of the techniques (find all current variables and trace them / source the scripts in child interp(s)) it would be possible to know which script first defined them (by comparing the set of defined variables after each script runs), but not the line number within those scripts. If you really need that badly enough (to justify the huge increase in execution cost), then you could do it with a leavestep trace - so your handler runs after each command executes, which would then compare the current snapshot of variables to find any new ones, and inspect the call frame stack for the line.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.