在if语句中使用字符串匹配的TCL / TK脚本问题

时间:2017-01-27 08:52:58

标签: bash tcl

我在bash中有一个脚本,它为我网络上的每个元素调用一个TCL脚本,该脚本根据元素的类型执行一些操作。这是代码的一部分,它检查主机名是否包含特定模式(例如* CGN01),然后向该机器提供适当的命令。

if {[string match "{*CGN01}" $hostname] || $hostname == "AthMet1BG01"} {
    expect {
    "*#" {send "admin show inventory\r"; send "exit\r"; exp_continue}
    eof
    }
}

使用上面引用的代码,当主机名是" PhiMSC1CGN01"时,我得到没有错误。然后if中的代码没有执行,这意味着表达式不正确。

我已尝试过所有内容(使用"()"或" {}"或" []"在if内部但是当我不放""在模式上我得到一个错误:

invalid bareword "string"
in expression "(string match {*DR0* *1TS0* *...";
should be "$string" or "{string}" or "string(...)" or ...
(parsing expression "(string match {*DR0* *...")
invoked from within
"if {$hostname == "AthMar1BG03" || [string match *CGN01 $hostname]...

或者这个:

expected boolean value but got "[string match -nocase "*CGN01" $hostname]==0"
while executing
"if {$hostname == "AthMar1BG03" || {[string match -nocase "*CGN01" $hostname]==0}...

当我尝试在表达式上使用== 0或== 1时。 我的TCL版本是8.3,我无法更新它,因为该机器没有互联网连接:(

请帮助我,我试图解决这个问题超过一个月......

2 个答案:

答案 0 :(得分:1)

如果要匹配正好为AthMet1BG01的字符串或任何以CGN01结尾的字符串,则应使用

if {[string match *CGN01 $hostname] || $hostname == "AthMet1BG01"} {

(对于Tcl 8.5或更高版本,请使用eq代替==。)

有关您尝试的一些评论:

(关于if使用的表达式语言的注释也适用于exprwhile。它在expr的文档中有详细说明。)

要在条件内调用命令并替换其结果,需要将其括在括号中([ ])。括号(( ))可用于设置条件中子表达式的优先级,但不表示命令替换。

通常,条件字符串内部需要用双引号或括号括起来({ })。这是因为用于表达条件的表达语言需要区分例如数字和字符串,Tcl一般不会。在条件中的命令替换内,只要字符串中没有您需要引用的字符,就需要使用引号或大括号。

字符串{abc}包含字符abc。字符串"{abc}"包含字符{abc},因为双引号使大括号成为正常字符(反之亦然)。 [string match "{*bar}" $str]匹配字符串{foobar}(大括号作为文本的一部分),但不匹配foobar

如果在命令替换{[incr foo]}周围添加大括号,则它变为字符串[incr foo],即不调用该命令并且不进行替换。如果您使用{[incr foo]==1},则会收到字符串[incr foo]==1。在表达式中写入此内容的正确方法是[incr foo]==1==周围有可选的空格。

这一切都有点难以理解,但是当你拥有它时非常容易使用。 Tcl顽固地说是解释弦乐的骡子,但是如果你对她正确的话会带来沉重的负担。

ETA 备用匹配器(请参阅注释)

您可以编写自己的备用字符串匹配器:

proc altmatch {patterns string} {
    foreach pattern $patterns {
        if {[string match $pattern $string]} {
            return 1
        }
    }
    return 0
}

如果任何模式匹配,则得到1;如果没有任何模式匹配,则得到0。

% altmatch {*bar f?o} foobar
1
% altmatch {*bar f?o} fao
1
% altmatch {*bar f?o} foa
0

对于那些拥有现代Tcl版本的用户,您实际上可以将其添加到string集合中,以便它像其他string命令一样工作。把它放在正确的命名空间中:

proc ::tcl::string::altmatch {patterns string} {
    ... as before ...

并像这样安装:

% set map [namespace ensemble configure string -map]
% dict set map altmatch ::tcl::string::altmatch
% namespace ensemble configure string -map $map

文档: exprstringSummary of Tcl language syntax

答案 1 :(得分:1)

此命令:

if {[string match "{*CGN01}" $hostname] || $hostname == "AthMet1BG01"} {

语法有效,但我真的不认为您想要将该模式与string match一起使用。我猜你真的想要:

if {[string match "*CGN01" $hostname] || $hostname == "AthMet1BG01"} {

该模式中的{大括号}实际上没有意义(string match只能完成全局匹配的全部功能的一部分)所以对于你的错误模式,你实际上是尝试匹配{开头的$hostname,任意数量的字符,然后CGN01}结尾$hostname 使用文字大括号。 只需移除大括号即可PhiMSC1CGN01匹配。