You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MobaXTerm连接数据库时Expect脚本set命令语法错误求助

问题:Expect脚本执行sqlcmd时触发参数数量错误

错误信息

wrong # args: should be "set varName ?newValue?"
    while executing
"set cmd sqlcmd -S '$server -d $database -U $username -P $password -Q '$sql_query''"
    (procedure "get_commands_from_db" line 10)
    invoked from within
"get_commands_from_db $server $database $username $password $sql_query"
    invoked from within
"set commands [get_commands_from_db $server $database $username $password $sql_query]"
    (file "./SampleTest3" line 46)

对应脚本代码

#!/usr/bin/expect

# Define database connection parameters
set server "App"
set database "comp"
set username "USERNAME"
set password "PASSWORD"

# Define the SQL query to fetch commands
set sql_query "SELECT Code FROM tblValues;"

# Define the list of servers
set servers {
    111.21.221.111
}

# Function to get commands from the database
proc get_commands_from_db {server database username password sql_query} {
    set commands_list [list]
    
    puts "DEBUG: Server: $server"
puts "DEBUG: Database: $database"
puts "DEBUG: Username: $username"
puts "DEBUG: SQL Query: $sql_query"
    
    # Prepare SQLCMD command
    set cmd sqlcmd -S '$server -d $database -U $username -P $password -Q '$sql_query''
    
    # Run SQLCMD and capture output
    set output [exec $cmd]
    
    # Split output into lines
    set lines [split $output "\n"]
    
    # Process lines and append to commands list
    foreach line $lines {
        if {[string length $line] > 0} {
            lappend commands_list $line
        }
    }
    
    return $commands_list
}

# Fetch commands from the database
set commands [get_commands_from_db $server $database $username $password $sql_query]

# Execute commands on remote servers
foreach server $servers {
    spawn ssh $username@$server
    expect {
        "password:" {
            send "$password\r"
            exp_continue
        }
        "==> " {
            send "1\r"
            exp_continue
        }
        "pcarr@idxhost ~> " {
            # Execute each command from the database
            foreach cmd $commands {
                send "$cmd\r"
                expect "$ "
            }
            send "exit\r"
        }
        "logout" {
            send "exit\r"
            exp_continue
        }
        "Connection closed" {
            send "exit\r"
            exp_continue
        }
        timeout {
            puts "Connection to $server timed out"
            continue
        }
    }
}

问题原因与解决思路

错误核心是set cmd行不符合Tcl/Expect语法规则:Tcl中set命令要求格式为set varName value,但原写法未将整个命令字符串用引号/大括号包裹,导致Tcl把sqlcmd、-S、'$server等拆分为多个参数传给set,触发参数数量错误;同时单引号嵌套会提前终止字符串,进一步加剧语法混乱。

修正方案1:双引号包裹并转义参数引号

将set cmd行修改为:

set cmd "sqlcmd -S \"$server\" -d \"$database\" -U \"$username\" -P \"$password\" -Q \"$sql_query\""

用双引号包裹整个命令,对每个参数的引号用\"转义,确保变量正确替换且sqlcmd能识别所有参数。

修正方案2:用Tcl list构造命令(更推荐)

Tcl的list能自动处理参数分隔与引号,避免字符串拼接的语法问题,exec可直接接收list作为命令参数:

set cmd [list sqlcmd -S $server -d $database -U $username -P $password -Q $sql_query]

这种方式无需手动转义,能可靠处理包含特殊字符的参数。

额外优化建议

若sqlcmd执行可能失败,可加catch捕获结果避免脚本崩溃:

if {[catch {exec {*}$cmd} output]} {
    puts "执行sqlcmd失败: $output"
    return $commands_list
}

其中{*}是Tcl的展开运算符,将list拆分为单个参数传给exec。

内容的提问来源于stack exchange,提问作者sw29

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 05:10:02