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! <>String Processing