if命令是用于基于特定情况执行脚本语言的。这篇文章将讨论if命令的规则和格式上的一些细节,以及在TCL语言中的应用。
 
命令用法
 
if expr1 ?then? body1 elseif expr2 ?then? body2 elseif ... ?else? ?bodyN
 
if命令将expr1作为一个表达式,来计算它的值(和expr计算它的参数值一样)。
表达式的值必须是布尔型(一个数值,0为false,其它的数字为true;或者是个字符串值,例如true or yes 为 true,以及 false or no 为 false).如果结果是true,那么通过TCL编译器执行body1.否则将expr2作为一个表达式,计算它的值,如果结果是true, 那么执行body2,以此类推。
如果所有的表达式的值都不为true,那么bodyN被执行。
Thenelse操作都是可选的语句(干扰字符),让命令更加明了。
在代码中也许会有许多的elseif子句,包括zero。
一旦else被省略了,bodyN也同样会被省略。
命令中返回的值是body脚本被执行后的结果,或者是一个空字符串,当没有一个表达式为非0,并且没有bodyN的时候。
 
例子
这是一种简单的情况

 

if { [HTTP::header Content-Length] > 0 } then {
  log local0. "Something's coming..."
}

 

如果你想增加一些状况,可以看这个例子

 

if { [HTTP::header Content-Length] > 0 } then {
log local0. "Something's coming..."
} elseif { [HTTP::uri] contains "foobar" } then {
 log local0. "The user wants some foobar!"
}

 

添加一种默认情况,就是当所有的if /else都没满足条件语句时,你可以增加一条else从句。

 

if { [HTTP::header Content-Length] > 0 } then {
 log local0. "Something's coming..."
} elseif { [HTTP::uri] contains "foobar" } then {
 log local0. "The user wants some foobar!"
} else {
 log local0. "I don't know what this user wants!"
}

 

同时,必须记住“then”和“else”是可选的,在以上的例子中,“else”和“if”删除了不会影响代码的功能。

 

if { [HTTP::header Content-Length] > 0 } {
 log local0. "Something's coming..."
} elseif { [HTTP::uri] contains "foobar" } {
 log local0. "The user wants some foobar!"
} {
 log local0. "I don't know what this user wants!"
}

 

对那些VB的程序员来说,还是喜欢留下“then”语句的吧B-).
同时,记住,表达式是可以分成多行写的,下面是一个运用then语句的很好的例子。

 

if {
 ([HTTP::uri] contains "foo") ||
 ([HTTP::uri] contains "bar")
} then {
 log local0. "found foo!"
}

 

表达式
 
“if”命令的核心是这些作为条件判断的表达式。这些表达式就像TCL“expr”命令给表达式估值一样被估值。下面是表达式的一些规则。
 
TCL表达式是由操作数,操作符和一些圆括号组成的。
空格符可以用在操作符,操作数和圆括号之间,空格表达式在执行时忽略。
在可能的情况下,操作数被编译成整形值。
整形可以有十进制(大多数情况),八进制(如果操作数的第一个字节是0),或者十六进制(如果操作数的头两个字节是0x)。
如果操作数不是以上所述的任一种形式,在可能的情况下它就被看做是浮点数。
浮点数应定义成任一种能符合ANSI标准的C编译器的格式(除了那些有f, F, l, 和L后缀的浮点数在大多数情况下不能被定义)。例如,以下所有都是有效的浮点数 2.1 , 3. ,6e4, 7.91e+16。
如果操作数不是数字(注意所有文字的操作数如果不是数字型,也不是布尔型,就必须用大括号或者双引号引用),那么操作数就是字符型(只有有限的操作数是这样的)。
操作数
 
操作数应被指定成下面的任一种类型。
 
1.作为数字型,必须是整形,或者浮点数。
2.作为布尔型,必须是可以被布尔型的字符串所识别的任一种形式。
3.作为TCL变量,应使用标准的$注释。变量的值必须是操作数。
4.作为附上双引号的字符串,表达式解析程序将会执行在双引号中引用的信息,包括反斜杠符号,变量,以及命令置换,并且算出其结果,再将其作为命令数。
5.作为在大括号中引用的字符串,在大括号内的特殊字符将被直接用作操作数,不做任何替换。
6.作为在括号中引用的TCL命令,命令将被执行,并将执行的结果作为操作数。
7.作为数学函数,它的参数有以上的任一种形式的,如abs($x) 见下文指定的函数列表。
 
操作符
以下是有效的操作数,以优先级递减的顺序排列
- + ~ !
一元减号,一元加号,位操作的非,逻辑非。所有的这些操作符都不能应用在字符操作数中,位操作的非只可应用在整形中。
* / %
乘号,除号,取余号。所有的这些操作符都不能应用在字符操作数中,取余号只可用在整形中。取余号和除数的形式是相同的,但是余数的绝对值比除数小。
+ -
加号和减号,可应用与任何的数字操作数。
<< >>
左右转移号。只对整形操作数有效。一个右转移号通常用于传送标记位。
< > <= >=
布尔型的小于,大于,小等于,大等于。如果条件语句为true,每个操作符为1,否则为0.这些操作符可以应用在字符串型和数字型操作符的字符串比较的情况。
== !=
布尔型的等于,和不等于。每个操作符的结果是1或者0.。可用于所有的操作数。
eq ne
布尔字符串的等于,和不等于。每个操作符的结果是1或者0.。操作数的类型只可用于字符串。
&
位操作的与.只可用与整形的操作数。
^
位操作的异或,只可用与整形的操作数。
|
位操作的或,只可用与整形的操作数。
&&
逻辑与。如果两个操作数都是非0,结果为1.否则为0.只对布尔型和数字型(整数或浮点数)操作数有效。
||
逻辑或。如果两个操作数都是0,结果为0,否则为1. 只对布尔型和数字型(整数或浮点数)操作数有效。
x?y:z
If-then-else,和C语言的定义一样。如果X的值为非0,那么这个表达式的值为Y,否则为Z。X操作数必须是布尔型或者数字型。
 
除了TCL语言中的基本操作符外,irules还添加了以下的操作符。
Contains
验证是否一个字符串中含有另一个字符串。
ends_with
验证是否一个字符串以另一个字符串结尾。
equals
验证是否一个字符串和另一个字符串相等。
Matches
验证是否一个字符串中能和第二个字符串匹配。
matches_regex
验证是否一个字符串和给定的正则表达式匹配。
starts_with
验证是否一个字符串以另一个字符串开头。
And
等同与逻辑与(&&)。
Not
等同与逻辑非(!)。
Or
等同与逻辑或(||)。
 
数学函数
 
以下的基于TCL语言的数学函数包含在iRules中。
abs(arg)
返回参数的绝对值。参数必须是整形或者浮点型,返回的值也必须是整形或浮点型。
double(arg)
如果参数是浮点型,返回参数的值,否则将参数转换成浮点型,并返回。
int(arg)
如果参数是整形值,并和机器语言有着相同的行幅,返回参数,否则裁剪参数,将其转换成整形后返回。
rand()
在(0,1)范围内返回一个随即浮点型值。发生器的算法是个简单的线性同余数生成程序,没有进行加密安全性处理。每个随机值得出的结果确定了随后调用产生的结果。所以不应用随机值产生一个密码序列,如一次使用的密码。发生器的随机种子数由内部机器时钟初始化产生,或者可以由初始化随机数发生器函数设定。
round(arg)
如果参数是整形,返回参数,否则通过凑整,转换参数为整形,并返回。
srand(arg)
整形的参数,被用来重新设定随机数字发生器产生随机种子数。返回随机种子数的第一个随机数字(见rand())。每个编译器有自己的随机种子数。
wide(arg)
如果参数不是64比特的整形,将其转变成至少64比特宽的整形值(如果参数是32比特宽,通过符号转换)。
 
类型,溢出和精密度
全部的包含整形的内部计算由C类型long处理,全部的包含浮点型的内部计算由C类型double处理。当转换一个字符串型到浮点型,会检测到指数溢出,并导致TCL错误。对于由字符型到整形的转换,溢出的检测取决于本地C库文件规则,因此这将被视为不可靠的。在任一种情况中,整形的溢出和下溢通常情况下对于中间结果来说不能可靠的被检测出来。浮点型的溢出和下溢,由硬件支持来决定,通常情况下是相当可靠的。
 
根据需要,整形,浮点型以及字符型操作数的内部表述的转换是自动完成的。在一些算数计算中,整形将一直使用直到引入了一些浮点型数字,在此之后,将一直使用浮点型计算。
例如:
expr 5 / 4
返回1,而
expr 5 / 4.0
expr 5 / ( [string length "abcd"] + 0.0 )
都返回1.25。 浮点型字符总是返回带有“.”的值,或者是个“e”,这样就不会看起来像个整形值。例如
expr 20.0/5.0
返回4.0,而不是4.
 
字符型操作数
 
字符型的值常被用作那些比较操作符的操作数,除了在有eq和ne这样的操作符的情况下,表达式的识别器在可能的情况下会尝试做整形和浮点型的比较。如果进行比较的操作数中的有一个是字符型,其他的是数字型,那么数字型的操作数,将被C语言的sprintf格式分符%d转换为整形和%g转换为浮点型。例如,命令
expr {"0x03" > "2"}
expr {"0y" < "0x12"}
都返回1.第一个比较计算是运用整形比较完成,第二个比较计算是运用字符串比较完成(通过将第二个操作数转换成字符串18)。因为TCL的倾向于在可能的情况下将值视为数字,当你实在想进行字符串的比较,并且操作数的值可以是任意的情况下,应用像“==”这样的操作数可不是个好主意,在这种情况下最好应用“eq”“ne”这样的操作符,或者用字符串命令代替。
 
性能方面的考虑
 
大括号内的表达式,有着最快的速度,而且占用最小的存储空间。这样TCL的字节码编译器就会产生最好的代码。
像前文提到过的一样,表达式被替换了2次。一次是TCL的语法编译器替换的,一次是由expr命令完成的。例如,命令
set a 3
set b {$a + 2}
expr $b*4
返回11,而不是4的倍数。这是因为TCL的语法编译器会最先替换变量b$a + 2,然后expr命令将计算表达式$a + 2*4.
 
大多数表达式不要求第二次替换。他们或者附上了括号,或着没有,他们的变量和命令产生数字或字符串,他们本身不需要替换。然而,由于一些没上括号的表达式需要二次替换,字节码编译器必须发出额外的指令来处理这种情况。那些没上括号的表达式需要一些对性能消耗最昂贵的代码,这些表达式包含有替换命令。每次执行表达式时,这些表达式都会产生新的代码。
参考iRules Optimization Tech Tip on Expressions and Braces,更加深入了解如何优化你的表达式。
 
结论
 
“if”申明是你写的任何iRule的基础。然而现在你是否了解这些细节并不是很重要,这篇文档可以作为你可以查阅的可靠的参考。
 
相关连接
 
Irules优化 #2–表达式和大括号-
TCL返回变量值文档
信任该信任的:这篇文档部分摘抄自TCL expr 文档