将表达式字符串参数作为大括号字符串提供是一种好习惯。标题“双重替代”概述了背后的重要原因。
该expr命令评估基于运算符的表达式字符串以计算值。该字符串是根据调用中的参数构造的。
expr 1 + 2 ; # three arguments expr "1 + 2" ; # one argument expr {1 + 2} ; # one argument
这三个调用是等效的,并且表达式字符串相同。
命令if,for和while使用相同的求值器代码作为条件参数:
if {$x > 0} ... for ... {$x > 0} ... ... while {$x > 0} ...
主要区别在于条件表达式字符串必须始终为单个参数。
与Tcl中命令调用中的每个参数一样,内容可能会也可能不会被替换,具体取决于如何对它们进行引用/转义:
set a 1 set b 2 expr $a + $b ; # expression string is {1 + 2} expr "$a + $b" ; # expression string is {1 + 2} expr \$a + \$b ; # expression string is {$a + $b} expr {$a + $b} ; # expression string is {$a + $b}
第三和第四种情况有所不同,因为反斜杠/花括号阻止替换。结果仍然是相同的,因为内部的求值器expr本身可以执行Tcl变量替换并将字符串转换为{1 + 2}。
set a 1 set b "+ 2" expr $a $b ; # expression string is {1 + 2} expr "$a $b" ; # expression string is {1 + 2} expr {$a $b} ; # expression string is {$a $b}: FAIL!
在这里,我们遇到了带有括号的参数的麻烦:当评估器expr执行替换时,表达式字符串已被解析为运算符和操作数,因此评估者看到的是一个由两个操作数组成的字符串,它们之间没有运算符。(错误消息是“ missing operator at _@_ in expression "$a _@_$b"”。)
在这种情况下,expr调用之前的变量替换可以防止错误。支撑参数可以防止变量替换,直到表达式求值,这会导致错误。
可能会发生这种情况,最典型的情况是将要求值的表达式作为变量或参数传入。在这些情况下,除了保留参数不加括号之外,没有其他选择,允许参数评估程序“解包”表达式字符串以传递给expr。
但是,在大多数其他情况下,将表达式括起来并没有害处,并且确实可以避免很多问题。一些例子:
双重替代
set a {[exec make computer go boom]} expr $a ; # expression string is {[exec make computer go boom]} expr {$a} ; # expression string is {$a}
无括号的形式将执行命令替换,该命令替换会以某种方式破坏计算机(或加密或格式化硬盘或您所拥有的东西)。大括号形式将执行变量替换,然后尝试(失败)创建字符串“ [exec make computer go boom]”。避免了灾难。
无休止的循环
set i 10 while "$i > 0" {puts [incr i -1]}
此问题同时影响for和while。尽管看起来该循环将倒数至0并退出,但while的条件参数实际上始终10>0是因为while激活命令时该参数被评估为该参数。将参数括起来后,它将作为传递给while命令$i>0,并且该变量将为每次迭代替换一次。使用此代替:
while {$i > 0} {puts [incr i -1]}
总体评价
set a 1 if "$a == 0 && [incr a]" {puts abc}
a运行这段代码后,值是多少?由于&&运算符仅在左操作数为true时才对右操作数求值,因此该值仍应为1。但实际上,它的值为2。这是因为在求值表达式字符串时,参数evaluator已经执行了所有变量和命令替换。使用此代替:
if {$a == 0 && [incr a]} {puts abc}
定义了多个运算符(逻辑连接词||和&&,以及条件运算符?:)以不求值其所有操作数,但只有在表达式字符串加括号时它们才能按设计工作。