【问题标题】:TCL/Expect - Output not showing up in buffer while expectingTCL/Expect - 预期时输出未显示在缓冲区中
【发布时间】:2015-04-21 07:00:12
【问题描述】:

好的,我在一家电信公司工作,我为我们的产品开发了一些自动化测试。我正在使用 TCL8.4 和 Expect 5.44.1.15,因为当我被聘用时有人问我。大多数测试包括使用 telnet 与我们产品的 shell 交互。所以我想出了下面的功能:

    # ------------------------------------------------------------------------------
# Description: Launch telnet session with specified IP and port and assign
#              resulting spawned process ID to processIDRef upon success.
# 
#  Parameters:       ipAddr - in:  IPv4 address to connect with.
#                      port - in:  Port number to connect with.
#                    prompt - in:  String indicating a successfull connection.
#              processIDRef - out: Resulting spawned process identifier.
#
#      Return: Returns no significant value.  Throws exception on failure.
# ------------------------------------------------------------------------------
proc ::connections::telnet::Connect {ipAddr port prompt processIDRef} {
    # Print function name and parameters
    ::utils::PrintDebug [info level 0]

    # Parameter reference(s)
    upvar $processIDRef processID

    # Validate parameters
    if {![regexp -- {^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}} $ipAddr]} {
        ::utils::Throw "Unsupported IP addresse format/value: \"$ipAddr\""

    } elseif {[string length $port] && !([string is integer $port] && $port >= 0)} {
        ::utils::Throw "Unsupported port number format/value: \"$port\""
    }

    # Spawn telnet session and expect prompt.
    if {![string length $port]} {
        # passing an empty string as port argument to telnet causes an error
        spawn telnet $ipAddr
    } else {
        spawn telnet $ipAddr $port
    }

    AddActiveSession $spawn_id

    Expect $spawn_id $prompt 30

    set processID $spawn_id
}

# ------------------------------------------------------------------------------
# Description: Close telnet session associated with processID.
# 
#  Parameters: processID - in: Spawned process identifier.
# 
#      Return: Returns no significant value.  Throws exception on failure.
# ------------------------------------------------------------------------------
proc ::connections::telnet::Disconnect {processID} {
    # Print function name and parameters
    ::utils::PrintDebug [info level 0]

    # Validate parameters
    if {![IsActiveSession $processID]} {
        ::utils::Throw "Unknown/Invalid processID: \"$processID\""
    }

    if {[::utils::IsProcessAlive $processID]} {
        # Open Telnet Prompt
        SendCommand $processID "\x1D" "" "telnet> " 10

        # Close telnet session
        SendCommand $processID "close" {\n} "Connection closed." 10

        # Remove from active sessions and close expect connection
        RemoveActiveSession $processID
        wait -i $processID
        close -i $processID

    } else {
        # Process is dead.
        RemoveActiveSession $processID
    }
}

# ------------------------------------------------------------------------------
# Description: Send command to process and expect response within alloted time.
#              Response can be collected inside a buffer if provided a reference.
# 
#  Parameters: processID - in: Spawnded process identifier.
#                command - in: String to be sent to process.
#             terminator - in: Character to append to command, signaling the 
#                              end of the command string for the interpreter.
#                 prompt - in: Regular Expression indicating end of command 
#                              execution.
#              timelimit - in: amount time we expect results before declaring
#                              failed execution.
#              bufferRef - out: Resulting command execution output.
#
#      Return: 1 if prompt was found within the timelimit, otherwise 
#              throws an exception.
# ------------------------------------------------------------------------------
proc ::connections::telnet::SendCommand {processID command terminator prompt timelimit {bufferRef ""}} {
    # Print function name and parameters
    ::utils::PrintDebug [info level 0]

    # Parameter reference(s)
    if {[string length $bufferRef]} {
        upvar $bufferRef buffer
    }
    set buffer ""

    # Set expect parameters
    set spawn_id $processID

    # Send command and expect results
    send -s "$command[subst $terminator]"
    Expect $processID $prompt $timelimit buffer
}

# ------------------------------------------------------------------------------
# Description: Expect response within alloted time.
#              Response can be collected inside a buffer if provided a reference.
# 
#  Parameters: processID - in: Spawnded process identifier.
#                 prompt - in: Regular Expression indicating end of command 
#                              execution.
#              timelimit - in: amount time we expect results before declaring
#                              failed execution.
#              bufferRef - out: Resulting command execution output.
#
#      Return: Returns no significant value.  Throws exception on failure.
# ------------------------------------------------------------------------------
proc ::connections::telnet::Expect {processID prompt timelimit {bufferRef ""}} {
    # Print function name and parameters
    ::utils::PrintDebug [info level 0]

    # Parameter reference(s)
    if {[string length $bufferRef]} {
        upvar $bufferRef buffer
    }
    set buffer ""

    # Validate parameters
    if {![IsActiveSession $processID]} {
        ::utils::Throw "Unknown/Invalid processID: \"$processID\""

    } elseif {!([string is integer $timelimit] && $timelimit >= 0)} {
        ::utils::Throw "Unsupported timeout format/value: \"$timelimit\""
    }

    # Set expect parameters
    set spawn_id $processID
    set timeout $timelimit

    # expect results
    expect {
        -re "$prompt" {
            # Command Successful
            if {[info exists expect_out(buffer)]} {
                append buffer $expect_out(buffer)
            }

            # Print Buffer to log file at least
            ::utils::PrintDebug $buffer
        }
        full_buffer {
            # expect_out(buffer) is full
            if {[string length $bufferRef]} {
                append buffer $expect_out(buffer)
            }
            exp_continue
        }
        timeout {
            # Timeout
            if {[info exists expect_out(buffer)]} {
                append buffer $expect_out(buffer)
            }

            # Print Buffer to log file at least
            ::utils::PrintDebug $buffer

            # Throw exception
            ::utils::Throw "Timed out while waiting for \"$prompt\"."
        }
        eof {
            # Process reached it's end
            if {[string length $bufferRef] && [info exists expect_out(buffer)]} {
                append buffer $expect_out(buffer)
            }

            # Print Buffer to log file at least
            ::utils::PrintDebug $buffer

            # Throw exception
            ::utils::Throw "Reached eof while waiting for \"$prompt\"."
        }
    }
}

基本上可以使用如下函数:

  • ::connections::telnet::Connect
    • 使用 telnet 连接到 IP 和端口并等待特定提示。
  • ::connections::telnet::Disconnect
    • 关闭远程登录。
  • ::connections::telnet::SendCommand
    • 通过 telnet 发送命令并期望在分配的时间内出现特定提示。
  • ::connections::telnet::Expect
    • 在分配的时间内期待某个提示而不发送命令。

问题:

有时会出现一个错误,我不知道是什么原因造成的。有时我使用 SendCommand 并且没有输出存储在期望缓冲区中,因此显然该函数超时并引发异常。除了如果我期待第二次,那么我们可以清楚地看到(在我的日志中)缓冲区现在确实包含我之前调用不超时所需的输出。

这是我的一些调试日志,向您展示它的样子:

Feb/18/2015 18:16:17: ::connections::telnet::SendCommand exp5 {~ restart dispGates} {\n} {----.*> } 10 缓冲区

2015 年 2 月 18 日 18:16:17:预期 exp5 {----.*> } 10 缓冲区

Feb/18/2015 18:16:27:这是记录缓冲区内容的地方,不应为空

Feb/18/2015 18:16:27:等待“----.*>”时超时。

...

Feb/18/2015 18:16:28: ::connections::telnet::SendCommand exp5 {~ *} {\n} {连接被外部主机关闭。} 30

Feb/18/2015 18:16:29: Expect exp5 {Connection closed by foreign host.} 30 buffer

这是我在最后一次通话中期待的输出!您可以清楚地看到我在此发送的两个命令。

Feb/18/2015 18:16:30: ~ 重启 dispGates

            RST_GATE_INITIAL     OPEN

  HardwareSettleCompleteGate     OPEN

            HsiAvailableGate     CLOSED

    DiagnosticsAvailableGate     CLOSED

           FirmwareReadyGate     CLOSED

    CardCommsDriverReadyGate     CLOSED

     ShelfCommsAvailableGate     CLOSED

DataProvisioningCompletedGate 已关闭

命令于 2003 年 1 月 7 日星期二 - 23:53:38 (UTC) 完成

----/重启> ~ * 连接被外部主机关闭。

当我尝试新事物时,这可能会一直发生,也可能会在我现有的任何功能中间歇性发生。我上面发送的命令在触发此错误之前的几分钟内已经运行了 10 多次。

这给我和我的同事以及使用我的包裹的客户带来了很多麻烦。我们有数万行代码使用它,现在越来越多的客户使用我们的测试套件,问题变得越来越明显。用另一种语言重写是我们最后的选择。

解决方法:

所以我尝试增加超时时间,但始终不起作用。

我也尝试过连续两次期待(以防第一次超时),但即使中间有延迟也无济于事。这就像我必须在再次调用它之前离开调用 ::connections::telnet::Expect 的函数。

我开发了一个函数来清除程序通道缓冲区和期望的缓冲区(因为似乎期望库中还没有包含一个)。有时它有帮助,有时它没有。即使它确实有 100% 的帮助,它也不是每次我们调用 send 命令时都使用的可行选项,因为它会将每个命令的执行减慢到至少 1 秒的执行时间。这里是:

# ------------------------------------------------------------------------------
# Description: Clears a process's channel buffer and expect_out(buffer).
#
#  Parameters: processID - in: Spawned process identifier.
#
#      Return: void - returns no signicant value.
# ------------------------------------------------------------------------------
proc ::utils::ClearBuffer {processID} {
    # Print function name and parameters
    PrintDebug [info level 0]

    # set expect parameters
    set spawn_id $processID
    set timeout 1

    catch {expect -re "thischainwillmostcertainlyneverappear"}
    catch {expect -re ".*"}
}

我的请求:

还有其他人遇到过这个问题吗?

为什么我的产品的输出不会像往常一样突然堆积在 expect 的缓冲区中?

是否有任何特殊字符可以告诉期望停止这样做?

还有什么我做错了吗?

还有其他解决方法的建议吗?

非常感谢您的宝贵时间。

【问题讨论】:

    标签: tcl buffer expect


    【解决方案1】:

    每当我遇到这种期望的问题时,我会首先使用 exp_internal 命令转储很多非常有用的关于缓冲区状态以及模式如何/是否匹配的信息。

    【讨论】:

    • 如果错误可以随意重现,这可能会很有用,但不幸的是,这种情况非常罕见。它大部分时间所做的只是阻止我的客户监控那里的测试结果。无论出于何种原因,expect 开发人员决定使用 -f 选项将复制输出而不是重定向它。有没有办法阻止这个输出在脚本中显示到终端?感谢您的帮助。
    • NVM 我明白了。如果您使用 -f 选项,无论您指定 0 还是 1 作为值,它都会记录信息。在我看来似乎违反直觉,但它确实有效。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-09-19
    • 2013-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-28
    相关资源
    最近更新 更多