知行合一
全部博文(31)
分类: 系统运维
2010-03-05 10:01:24
1.6及其以后的版本,我们将整数替换为浮点数来进行表达式的计算。可能的话,我们使用‘long double’,long double包括12个字节,提供16个数字的数字精度。
要指定一个浮点数常量,必须使用下面的格式:D.D,D是10进制的一个字符串。你可以写成0.10,但是不能写成.10或是20. – 我们希望这不是一个过分的限制!
可以使用printf函数的’%g’/’%Lg’将浮点数转变成字符串。这就允许这些数字在那些依靠整数进行操作的行为中看起来仍然像一个整数。如果你想将1/4求值为0,需要写成这样TRUNC(1/4)。我们会在下一节列出asterisk所有的截断/凑整的能力(truncation/rounding capabilities)。
1.6以后的版本,我们升级了$[]表达式,使其处理浮点数。因此,传统的使用整数进行的操作就变得异常了。为了使结果一致成为可能,一些凑整和整数截断函数被添加进了Expr2解析器的核心部分。的确,拨号方案函数不需要${}操作符也可以从$[…]表达式被调用了。唯一的麻烦可能就是这些函数的参数必须用逗号来指定。譬如,如果你试着调用MATH函数,3 + MATH(7*8),表达式解析器会将7*8求值为56提供给你,MATH函数很可能会抱怨它的输入没有任何意义。
我们也提供了一些访问C库中的浮点数函数的方法(不包括所有的函数)。
我们不希望人们在拨号方案中做傅立叶分析,但是我们也不会阻止他们。
下面是Expr2的一份内建函数的列表。所有的其他的拨号方案函数可以简单的通过调用它们来获得。换句话说,你不再需要使用${}在$[]中将函数包起来。但是,千万不要现在下结论—— 因为你仍然需要用花括号将变量名给括起来。
1.COS(x) x 是弧度,结果值从-1到1
2.SIN(x) x是弧度,结果值从-1到1
3.TAN(x) x 是弧度
4.ACOS(x) x 应该是一个从-1到1的值
5.ASIN(x) x应该是一个从-1到1的值
6.ATAN(x) 以弧度表示返回反正切值; 值介于-PI/2和PI/2之间
7.ATAN2(x,y) 除非两个参数被用来决定结果所在的象限,否则返回一个类似于y/x的结果。结果值以弧度表示,介于-PI和PI之间。
8.POW(x,y) 返回x的y次幂。
9.SQRT(x) 返回x的平方根。
10.FLOOR(x) 将x向下凑整为最接近的整数。
11.CEIL(x) 将x向上凑整为最接近的整数。
12.ROUND(x) rounds x to the nearest integer,
but round halfway cases away from zero.
13.RINT(x) rounds x to the nearest integer,
rounding halfway cases to the nearest even integer.
14.TRUNC(x) 将x凑整为不大于实际值的最接近的整数。
15.REMAINDER(x,y) computes the remainder of
dividing x by y. The return value is x - n*y, where n is the value x/y, rounded
to the nearest integer. If this quotient is 1/2, it is rounded to the nearest
even number.
16.EXP(x) 返回e的x次幂。
17.EXP2(x) 返回2的x次幂。
18.LOG(x) 返回x的自然对数。
19.LOG2(x) 返回以2为基数的x的log值。
20.LOG10(x) 返回以10为基数的x的log值。
"One Thousand Five Hundred" =~
"(T[^ ]+)"
返回: Thousand
"One Thousand Five Hundred" =~
"T[^ ]+"
返回: 8
"One Thousand Five Hundred" :
"T[^ ]+"
返回: 0
"8015551212" : "(...)"
返回: 801
"3075551212":"...(...)"
返回: 555
!
"One Thousand Five Hundred" =~ "T[^ ]+"
返回: 0
!(
"One Thousand Five Hundred" : "T[^ ]+" )
返回: 1
2 +
8 / 2
返回6.
2+8/2
返回6.
(2+8)/2
返回5
(3+8)/2
返回5.5.
TRUNC((3+8)/2)
返回5.
FLOOR(2.5)
返回2
FLOOR(-2.5)
返回-3
CEIL(2.5)
返回3.
CEIL(-2.5)
返回-2.
ROUND(2.5)
返回3.
ROUND(3.5)
返回4.
ROUND(-2.5)
返回-3
RINT(2.5)
返回2.
RINT(3.5)
返回4.
RINT(-2.5)
返回-2.
RINT(-3.5)
返回-4.
TRUNC(2.5)
返回2.
TRUNC(3.5)
返回3.
TRUNC(-3.5)
returns -3.
当然,上面的所有例子使用的都是常量,但是如果使用变量引用来替代数字或字符串常量,效果是一样的,譬如${CALLERID(num)}。
完全由数(numbers)组成的标记如果可能的话,会转换成’long double’类型,根据操作系统,编译器和硬件的不同,有80到128位不等。这就意味着当数(numbers)超过18个数字(digits)时,可能会发生溢出(根据位数的不同有所不同)。这种情况下,警告将被记录在log文件中。
有一种条件语句的应用——按条件goto到不同的标签(labels)
exten =>
1,2,GotoIf(condition?label1:label2)
如果条件为真,则转到label1,否则转到label2。在通常的goto命令中,标签都会被正确的解释。
“条件”仅仅是一个字符串。如果该字符串为空或为“0”,则被认为是假(false),否则被认为是真(true)。这种设计被用来和上面描述的表达式语法一起使用,如:
exten =>
1,2,GotoIf($[${CALLERID(all)} = 123456]?2,1:3,1)
其他的例子:
exten => s,2,Set(vara=1)
exten => s,3,Set(varb=$[${vara} + 2])
exten => s,4,Set(varc=$[${varb} * 2])
exten => s,5,GotoIf($[${varc} = 6]?99,1:s,6)
目前语法错误会有3行输出信息。
如果在extensions.conf文件中包含如下的内容:
exten =>
s,6,GotoIf($[ "${CALLERID(num)}"
= "3071234567" & & "${CALLERID(name)}" :
"Privacy Manager" ]?callerid-liar,s,1:s,7)
日志中的信息告诉你碰到了一个语法错误。它还告诉你(以一种极其标准的bison格式),它遇到了一个没有意料到的”AND(&)”标记,而它希望是一个MINUS(-),LP(左括号)或是一个无格式标记[plain token](字符串或数字)
接下来的一行显示了被求出的表达式的值,之后的行信息,以及由“^”字符标记的遇到的解析错误的位置信息。
判断一个字符串是否为null可以通过下面的两种方式:
exten => _XX.,1,GotoIf($["${calledid}" !=
""]?3)
或是
exten => _XX.,1,GotoIf($[foo${calledid} != foo]?3)
上面的第二种方法是WIKI中建议的。只要所求值中没有空格,就有效。第一种方法在任何情况下都可以使用,事实上,它可能是目前处理这种情况的最安全的方法。
如果你需要使用字符串来做一些复杂的事情,asterisk表达式不是最好的选择。AGI脚本可能是处理这种需要的最佳选择,它可以使你发挥出你确定使用的编程语言的完全的能力(Perl, C, C++, Cobol, RPG, Java, Snobol,
PL/I, Scheme, Common Lisp, Shell scripts, Tcl, Forth, Modula, Pascal, APL,
assembler, 等。)
Asterisk的表达式解析器已经经历了一些变化。希望这些变化是积极的。
原来的表达式解析器有一个简单的,手写的(hand-written)扫描器和一套简单的bison语法。现在已经升级到一套更加复杂的bison语法和一个允许空格并能够产生更好的错误诊断的手写扫描器。但是相应的需要升级到bison 1.85版本。
接着需要升级的包括新的bison和flex的输入文件(input files),makefile文件也被升级到可以侦测flex和bison的当前版本,版本符合的话它会对新的文件进行编译和链接。
如果你已经1年没有碰你的extensions.conf文件了,那么上面的升级动作可能会给你带来一些头疼的情况,因为已经做过一些变化,而这些变化将影响到asterisk的一些行为以及遗留下来的extensions.conf的构造。虽然这些变化已经设计的尽可能的减小了这些影响,但是却仍然是个问题。
下面的列表给出了一些extensions.conf文件中遗留的一些可能会有影响的内容:
1.空格分隔的标记(token)。之前,标记是由空格来进行分隔的。因此,’1 +
2.冒号操作符。双引号之前的版本,冒号将右方的字符串当作正则表达式的模式(regex pattern),用它来和左边的字符串进行匹配。在开头有一个隐式的^操作符,它的意思就是只从左边字符串的开头进行匹配。如果正则表达式模式或是欲匹配的字符串有双引号引起来,那么它们(double quotes)也参与到匹配当中。而现在,在匹配之前,双引号将被移除。之所以这么做,是因为扫描表达式的新的方式是不再使用空格来进行对标记的分隔,并且正则中的操作符将被扫描器当作表达式操作符。因此,除非该模式被双引号引起来,否则将会有麻烦。譬如,${VAR1} : (Who|What*)+在以前的版本中可能正常有效,但是现在,除非你加上双引号,否则就要当心了!像下面这样”${VAR1}”:”(Who|What*)+”将能像以前一样正常有效。
3.Variables and Double Quotes Before these changes, if a variable's value
contained one or more double quotes, it was no reason for concern. It is now!
4.LE, GE, NE操作符被移除。以前的代码支持这些操作符,但是没有被写入文档。取而代之的是符号操作符 <=, >=和!=。
5.增加了一元’-’操作符。所以你可以像这样写3+-4从而得到-1。
6.增加了一元’!’操作符(逻辑补码)。基本的,如果字符串或数字为null,空或’
7.增加了’=~’操作符,这是为了可以去匹配字符串中任何位置的子字符串。它和’:’操作符的唯一区别就在于它不用从字符串的开头开始匹配。
8.增加了条件操作符’expr1 ? true_expr : false_expr’。首先,所有的3个exprs都会被求值,如果expr1为假(false),结果将返回’false_expr’,否则返回’true_expr’。要了解详细内容,可以看前面章节。
9.一元操作符’-’和’!’能够被正确的关联使用。
你可以构建两个实用的工具来帮助你调试extensions.conf文件中的$[]。
首先,也是最简单的,就是执行下面的命令:
make testexpr2
在asterisk源码的文件夹的顶层目录中,上面的命令将构建一个小的可执行文件,这个可执行文件将运行表达式解析器。不会有变量替换产生。这可能是最安全的将表达式用单引号引起来的方式。例如:
testexpr2 '2*2+2/2'
在utils文件夹中,你可以输入:
make check_expr
于是,一个小的程序被构建,这个程序将检查第一个命令行参数提到的文件,当你使用flex-
一个新的功能被加进了check_expr,这个功能可能更加有用。它对所有的变量进行简单的有意的求值,然后将$[]exprs传给解析器。如果有任何的解析错误,它们将被写进log文件。你可以用check_expr对你的extensions.conf文件进行合理的检查,从而发现一些语法错误。
简单有意(simple-minded)的变量替换用类似’
check_expr /etc/asterisk/extensions.conf
CALLERID(num)=3075551212 DIALSTATUS=TORTURE EXTEN=121
将使用3075551212来替代文件中的任何${CALLERID(num)}变量引用,’TORTURE’替代${DIALSTATUS},’
check_expr /etc/asterisk/extensions.conf
CALLERID(num)=3075551212 DIALSTATUS=TORTURE EXTEN:2=121
你将看到在标准输出stdout中显示:
OK -- $[ "${DIALSTATUS}" = "TORTURE" |
"${DIALSTATUS}" = "DONTCALL" ] at line 416
在生成的expr2_log文件中,你将看到:
line 416, evaluation of $[ "TORTURE" = "TORTURE" | "TORTURE" =
"DONTCALL" ] result: 1
check_expr只是一个很简单的算法,它不能解决所有的问题,但是希望它对于你的工作有用。