tcl 字符串操作 替换
CL:字符串命令
有一个关于TCL的哲学探讨:TCL语法中的一切是否都是字符串?(以及这是不是一件好事),但是无论你的论点如何,你都无法否认字符串在TCL语法中扮演着非常重要的角色,iRules也是一样。TCL提供了许多的字符串命令,其中的一些会在本文中提及。
我们将会看到这几条命令的几个具体案例,主要是因为我仍然觉得其它人在TCL文档中使用的例子实在是太少了。
string map ?-nocase? mapping string
在字符串中,替换子串是其于映射中的Key-Value对。映射是指一个key value key value ... 的列表,字符串中每一个Key的实例都会被它所对应的Value所取代。如果指定了-nocase模式,那么匹配就会执行,而不考虑大小写的差异。Key和Value都可能是一个多重字符。替换是按特定的顺序进行的,所以在列表中第一个出现的Key就会被第一个选中,依此类推。下面的一个例子是由论坛网友Hoolio提供的,有两种映射模式,用Https取代Http,并把服务器端口号去掉。
?
? when HTTP_RESPONSE {?????? # Check if a response is a redirect 查看响应是不是一个重定向。
?????? if {[HTTP::is_redirect]}{ ????????? log local0. "Original Location: [HTTP::header value Location]"
????????? # Assume the server will use it's own TCP port in redirects and remove it.? Also replace http:// with https://.
#假设服务器会在重定向时使用它自己的TCP端口并把它去掉。同时用https:// 替换http://
????????? HTTP::header replace Location [string map -nocase ]list http:// https:// ":[LB::server port]" ""[ ]HTTP::header value Location[]
????????? log local0. "Updated location (string map): [string map -nocase ]list http:// https:// ":[LB::server port]" ""[ ]HTTP::header value Location[]"
????? }?
?? }?
字符串只进行一次迭代,所以先前替换的Key不会影响到之后的Key匹配。例如:
% string map {abc 1 ab 2 a 3 1 0} 1abcaababcabababc
01321221
什么?这是TCL文档里的一个不那么直观的例子。但事实上,我非常喜欢它。我们来详细的分析一下。这个例子里有四个key/value对
?
String Map Multiple Key/Value Example
Mapping
Original String
Resulting String
1st (abc->1)
1abcaababcabababc
11aab1abab1
2nd (ab->2)
11aab1abab1
11a21221
3rd (a->3)
11a21221
11321221
4th (1->0)
11321221
01321221
?
注意看第四组映射,返回的字符串是01321221,而不是00320220。为什么呢?这是因为字符串只进行一次迭代,所以先前替换的Key不会影响到之后的Key匹配。
string range string first last
?
返回字符串里某一范围内的连续字符,以索引里的第一个字符起始,以索引里的最后一个字符结束。索引值为零指的就是字符串里的第一个字符,起始和结束是由索引方式来指定的。如果起始值小于零,那就当它当做零来处理,如果结束值大于等于字符串长度,就把它当做结尾。如果起始值大于结束值,则会返回一个空的字符串。Colin成功的提出了一个通过字符串范围来删除Host里的已知端口的例子。请注意他非常漂亮的使用了0作为起始,以end-5作为结束。当然,这个问题可以通过getfield命令更简便,更轻松的解决,但那是另一回事。
when HTTP_REQUEST {? if { [HTTP::host] ends_with "8010" } {??? set http_host [string range ]HTTP::host[ 0 end-5]
??? HTTP::redirect "https://$http_host[HTTP::uri]"
? }
}
在这个例子里,网友unRuleY提供了一个去掉URI中最前面几个字符的解决方案:
when HTTP_REQUEST {?? set uri [HTTP::uri]
?? if { $uri starts_with "/axess2" } {????? HTTP::uri [string range $uri 7 end]
????? pool pool1
?? }
}
string trim string ?chars?
字符串剪切 字符串?符号?
返回一个除了删除出现在字符串首尾的字符,其余都和原来一样的字符串。如果没有指定符号,那么空白(空格,tab,换行和回车)会被删除。在我刚开始写iRules的时候,我并不理解这些,以及trimleft和trimright。trimleft和trimright分别用来删除串首和串尾的字符,但trim可以同时删除首尾的字符。这里有些Shell下的例子来解释这一行为:
% set a ". this is a string ."
. this is a string .
% string trim $a .
?this is a string????????????????????????????????????????? # notice the . was trimmed, but not the whitespace注意.被剪切掉了,但是空格没有
% string trim [string trim $a .] " "
this is a string?????????????????????????????????????????? # now the whitespace has been trimmed (both leading and trailing)现在首和尾的空格都被剪切了
% string trim [string trim $a .]
this is a string?????????????????????????????????????????????? # same as before, the " " isn't necessary, removed by default when ?chars? isn't specified??????????????? 和上面的例子一样,只是没有必要使用“ ”,不指定符号时候一样可以默认的进行删除。
% set b [string trim ]string trim $a .[]<EOS>
this is a string<EOS>??????????????????????????????????? # Just added <EOS> to show the whitespace has in fact been trimmed from the trailing end of the string?????????? 添加了一个<EOS>来显示字符串末尾的空格的确是被去掉了。
%
Trimleft和trimright的工作方式是一样的。他们在匹配时并不剪切字符串首尾的字符,而是将匹配上的字符删掉。这里有一个Colin的例子,他用trimleft命令删除了路径中的第一个字符。需要注意的是,我们在上面那个使用字符串范围的例子里也解决了同样的问题。你将会遇到的问题中的大部分都是有多种解决方法的,在这个例子中,路径是指定的,这就使得Rules要比字符串范围的命令更简单,尤其是你所有的路径都以/xyz/开始的时候。
when HTTP_REQUEST {? if {[HTTP::host] contains "soa"} {??? if {[HTTP::uri] starts_with "/prd/"} {????? HTTP::uri [string trimleft ]HTTP::uri[ /prd]
????? pool POOL_SOA_PRD
??? }
??? if {[HTTP::uri] starts_with "/ppd/"} {????? HTTP::uri [string trimleft ]HTTP::uri[ /ppd]
????? pool POOL_SOA_PPD
??? } else {????? # THIS IS ONLY HERE SO THAT THEY CAN KEEP TESTING
????? pool POOL_SOA_PRD
??? }
? }
}