Here comes the code for the extension to [subst] introduced in [extending the notation of proc args]... The documentation is written in [tcldoc]. ====== # provides a fail-safe {@link http://www.tcl.tk/man/tcl8.5/TclCmd/subst.htm # http://www.tcl.tk/man/tcl8.5/TclCmd/subst.htm} which optionally performs # substitutions in an uplevel. If -inplace, then false is returned # if any call to ::subst fails. All variables are handled anyways. # @param -nocomplain in case of an error, the initial value is returned and no error is thrown # @param -uplevel the level at which substitutions are performed. Defaults to the current context # @param -inplace all non-switch arguments at the end are variable names in the caller's context. # Their value is replaced and true or false is returned # @param -- optionally used to separate switches from other parameters # @param args forwards all args defined for ::subst, # but allows multiple strings or variable names # @return the value of the last argument after performing TCL substitutions # @see http://www.tcl.tk/man/tcl8.5/TclCmd/uplevel.htm # http://www.tcl.tk/man/tcl8.5/TclCmd/uplevel.htm proc subst {args} { set level 0 set complain true set inplace false set switches {} for {set i 0} {$i < 7} {incr i} { set c [lindex $args $i] switch $c { -uplevel {set level [lindex $args [incr i]]} -nocomplain {set complain false} -inplace {set inplace true} -nobackslashes - -nocommands - -novariables {lappend switches $c} default { if {$c eq {--}} {incr i} break } } } set args [lrange $args $i end] catch {incr level} # 4 paths for -nocomplain and -inplace if {$inplace} { set ret true foreach args $args { upvar $args myvar if {[catch {uplevel $level [list ::subst $myvar]} result options]} { if {$complain} { return {*}$options $result } else { # TODO: log error? set ret false } } else { set myvar $result } } } else { set ret {} foreach args $args { if {[catch {uplevel $level [list ::subst $args]} result options]} { if {$complain} { return {*}$options $result } else { # TODO: log error? lappend ret $args } } else { lappend ret $result } } } return $ret } ====== For redirection see: [Overloading Proc] ---- [samoc] 20140612: Here is another `subst` replacement that adds a `-nocomplain` option to ignore unknown variable names. ====== rename subst tcl_subst proc subst_nocomplain {args} { try { uplevel tcl_subst $args } trap {TCL LOOKUP VARNAME} {msg info} { lassign [dict get $info -errorcode] - - - var set args [string map [list \$$var \\\$$var] $args] uplevel subst_nocomplain $args } } proc subst {args} { if {[set i [lsearch [lrange $args 0 end-1] -nocomplain]] != -1} { uplevel subst_nocomplain [lreplace $args $i $i] } else { uplevel tcl_subst $args } } ====== e.g. ====== % set v1 hello % set v2 world % subst -nocomplain {$v1 $v2 $v3} hello world $v3 ====== I find this useful in code-generation / template expansion situations. The following further modification handles unknown commands. However, I'm not quite happy with the way this works. It relies on `regexp` match of the human-readable "invalid command name" message (it seems there is no -errorcode for this error). Also, just escaping the `[` works for trivial situations, but can have unexpected results in some cases. See example below... ====== proc subst_nocomplain {args} { try { uplevel tcl_subst $args } trap {TCL LOOKUP VARNAME} {msg info} { lassign [dict get $info -errorcode] - - - var set args [string map [list \$$var \\\$$var] $args] uplevel subst_nocomplain $args } on error {msg info} { if {[regexp {invalid command name "(.*)"} $msg - cmd]} { set args [string map [list \[$cmd \\\[$cmd] $args] uplevel subst_nocomplain $args } else { return -code error -options $info } } } ====== e.g. ====== proc bar {args} { string toupper $args } set v1 hello set v2 world puts [subst -nocomplain {$v1 [foo $v2] [foo [bar a b c]] $v3}] hello [foo world] [foo A B C] $v3 puts [subst -nocomplain {$v1 [bar [foo $v2] xx] [foo [bar a b c]] $v3}] hello {[FOO} WORLD xx] [foo A B C] $v3 ====== [AMG]: Bug reported [http://core.tcl.tk/tcl/tktview/311e61d12ad1eb6355c13d2d2ed4acf1c45c4557], thanks! [AMG]: Why are you modifying the entire $args list? Only the last element contains the string to be [subst]'ed. [AMG]: Here are some more cases that don't work: ====== subst -nocomplain {[ foo ]} subst -nocomplain {[expr 42; foo]} subst -nocomplain {[if {1} {foo}]} ====== Things can get arbitrarily fancy here. I'm not sure what exactly you want each of the above examples to return. I'm puzzled about where the close bracket went following WORLD in your last example, or why xx isn't capitalized. <>String Processing