Quoting hell

Summary

Quoting Hell is where you are when you've backed yourself into a corner, and now find yourself desperately escaping escape characters in your programming language, clinging to the hope that if you add another backslash or two, "it might work". In some languages, this is the natural state of things. In Tcl, it happens when you're on the newbie learning curve and haven't yet come to the a-ha! moment about the code/data duality of Tcl values. You know you're in quoting hell when your Tcl code begins to look like Perl. Thankfully, unlike some other languages, quoting hell can be mostly avoided in Tcl. To learn how, digest the contents of Tcl Quoting!

Examples

Bad (from the Macports file macports.tcl):

eval curl fetch $verboseflag {$source} {$tarpath}

Fixed:

eval [list curl fetch $verboseflag $source $tarpath]

APN The above two lines are not equivalent if $verboseflag is not a single "word". If it is known to be a single word, it is not clear to me why an eval is needed at all.

AMG: My assumption is that $verboseflag is either "-verbose" or empty string. So yes, in that case the two are not equivalent. Instead try:

eval curl fetch $verboseflag [list $source $tarpath]

Or even better, use {*}:

curl fetch {*}$verboseflag $source $tarpath

I find that {*} obsoletes [eval] in almost every practical case.


Bad (from the [Macports file macports.tcl):

$workername eval set $opt \{[set $opt]\}

Fixed:

$workername eval [list set $opt [set $opt]]

Bad (from the [Macports file macports.tcl):

$workername eval set system_options($opt) \{[set $opt]\}

Fixed:

$workername eval [list set system_options($opt) $opt]

Bad (from the MacPorts file macports.tcl):

$workername eval "proc PortSystem \{version\} \{ \n\
    package require port \$version \}"

Fixed:

set script {
    proc PortSystem {version} {
        package require port ${version}
    }
}
set script [string map  [list \${version} [list $version]] $script]
$workername eval $script

glennj: even that is excessively quoted. I would write:

set script [string map [list {${version}} $version] $script]                                               

or, assuming you want to substitute values for all variables:

set script [subst -nocommands -nobackslashes $script]

PYK 2016-03-29: That won't fly. Consider a value of 1 a for $version, which results in too many arguments:

package require port 1 a

To substitute a value into script as an individual word in that script, format the value as a list containing one item:

set script [string map [list {${version}} [list $version]] $script]                                               

See Also

Tcl minimal escaping style
Quoting Heaven!
Mirror universe alternative formulation. Of extreme worth on this page is DGP's objection.