在期望中终止生成会话

Posted

技术标签:

【中文标题】在期望中终止生成会话【英文标题】:Terminating spawn sessions in expect 【发布时间】:2013-11-15 10:38:09 【问题描述】:

我正在尝试解决登录大量设备(数千个)的 Expect 脚本的问题。该脚本大约有 1500 行,并且相当复杂;它的工作是审核具有数千个节点的网络上的受管设备。结果,它通过 telnet 登录设备,运行命令检查设备的健康状况,将此信息记录到文件中,然后注销以继续下一个设备。

这是我遇到问题的地方;我脚本中的每个 expect 都包含一个超时和一个 eof,如下所示:

timeout 
    lappend logmsg "$rtrname timed out while <description of expect statement>"
    logmessage
    close
    wait
    set session 0
    continue

eof 
    lappend logmsg "$rtrname disconnected while <description of expect statement>"
    logmessage
    set session 0
    continue

我的最终expect 手动关闭每个生成会话:

-re "OK.*#" 
    close
    send_user "Closing session... "
    wait
    set session 0
    send_user "closed.\n\n"
    continue

假设 session = 0,继续将脚本带回到启动下一个生成会话的 while 循环。

设置会话 0 跟踪生成会话何时通过超时手动关闭或在新生成会话打开之前通过 EOF 关闭,并且一切似乎都表明生成会话正在关闭,但在大约一千个生成会话之后,我收到以下错误:

spawn telnet <IP removed>
too many programs spawned?  could not create pipe: too many open files

现在,我是一名网络工程师,而不是 UNIX 管理员或专业程序员,那么有人可以帮助我解决我的错误吗?我是否正在关闭 telnet spawn 会话但没有正确关闭频道?我编写了第二个测试脚本,它实际上只是一个接一个地连接到设备,并在连接形成后立即断开连接。它不像我的主脚本那样登录或运行任何命令,它通过数千个连接完美地工作。该脚本如下:

#!/usr/bin/expect -f

#SPAWN TELNET LIMIT TEST

set ifile [open iad.list]
set rtrname ""
set sessions 0

while [gets $ifile rtrname] != -1 
set timeout 2
spawn telnet $rtrname
incr sessions
send_user "Session# $sessions\n"
expect  
    "Connected" 
                close
                wait
                continue
                
    timeout     
                close
                wait
                continue
                
    eof         
                continue
                

在我的主脚本中,我正在记录每个连接以及它们可能出现 EOF 或超时的原因(通过将特定原因写入文件的 logmessage 进程),即使我只看到成功的生成连接和关闭的连接,我的主脚本遇到了同样的问题,但测试脚本却没有。

我一直在阅读有关终止进程 ID 的内容,但据我了解,close 应该会终止当前生成会话的进程 ID,然后 wait应该暂停脚本,直到进程结束。我还尝试使用设备中的简单“退出”命令来关闭 telnet 连接,但这并没有产生更好的结果。

我可能只需要关于如何更好地跟踪我的会话的打开和关闭并确保在设备之间没有生成会话保持打开状态的建议。任何可以提供的帮助将不胜感激。

谢谢!

【问题讨论】:

这篇文章"Too Many Open Files error message" 可能会有所帮助。 每次打开新会话时,PID 都会发生变化。在我的近距离等待之前,我正在尝试执行 kill $pid。我现在正在运行几千台设备,看看是否能解决它;显然,一些旧版本的 telnet 命令并不总是正确地关闭它们的进程。 我不相信将continue 放在eof 子句中是个好主意,但我对Expect 的了解仍然有点零散…… 我还是有这个问题。杀死 PID 似乎仍然不能解决问题。我的测试脚本在 eof 中使用 continue 没有问题。 @joshua-briefman 给出了一个很好的答案。我建议考虑使用 ssh 而不是 telnet。这将提高安全性。您将获得额外的好处,即 ssh 更像您所期望的那样关闭,而不是 telnet 使用的两步过程。 【参考方案1】:

错误?

spawn telnet 生成的程序太多?无法创建 管道:打开的文件太多

此错误可能是由于您的系统用尽了文件句柄(或至少耗尽了您可用的数量)。

我怀疑造成这种情况的原因是被遗弃的 telnet 会话保持打开状态。

现在让我们谈谈为什么他们可能还在闲逛。


不均匀,关闭?

Close 可能不会真正关闭 telnet 连接,特别是如果 telnet 无法识别会话已关闭,则只期望与 telnet (See: The close Command) 的会话。在这种情况下,Telnet 很可能会一直保持活动状态,等待来自网络端的更多输入和 TCP 保持活动。

并非所有应用程序都能识别关闭,它作为 EOF 呈现给接收应用程序。因此,即使输入已关闭,它们也可能保持打开状态。

告诉“Telnet”,结束。

在这种情况下,您需要中断 telnet。如果您的意图是完成一些工作并退出。那么这正是我们需要做的。

对于“telnet”,您可以通过发出“send “35\r””(如果您必须自己键入,则在键盘上为“ctrl+]”)然后是“quit”和一个回车来干净地退出返回。这将告诉 telnet 正常退出。

Expect script: start telnet, run commands, close telnet 摘录:

#!/usr/bin/expect
set timeout 1
set ip [lindex $argv 0]
set port [lindex $argv 1]
set username [lindex $argv 2]
set password [lindex $argv 3]
spawn telnet $ip $port
expect “‘^]’.”
send – – “\r”
expect “username:” 
    send – – “$username\r”
    expect “password:”
    send – – “$password\r”

expect “$”
send – – “ls\r”
expect “$”
sleep 2
# Send special ^] to telnet so we can tell telnet to quit.
send “35\r”
expect “telnet>”
# Tell Telnet to quit.
send – – “quit\r”
expect eof
# You should also, either call "wait" (block) for process to exit or "wait -nowait" (don't block waiting) for process exit.
wait

等待,等待结束。

Expect - The wait Command

如果没有“等待”,expect 可能会过早地切断与进程的连接,这在极少数情况下会导致创建僵尸。如果应用程序没有更早地收到我们的信号(来自关闭的 EOF),或者如果进程没有将 EOF 解释为退出状态,那么它也可能会继续运行,而您的脚本也不会更明智。通过等待,我们确保在清理并退出之前不会忘记该过程。

否则,在期望退出之前,我们可能不会关闭任何这些进程。如果长时间运行的期望脚本(或连接到许多服务器的脚本)没有关闭文件句柄,这可能会导致我们用完文件句柄。一旦我们用完了文件句柄,expect 和它开始的所有东西都会死掉,你不会再看到这些文件句柄用尽了。

超时?,全部捕获?,为什么?

您可能还想考虑使用“超时”,以防服务器未按预期响应,以便我们可以提前退出。这对于严重滞后的服务器来说是理想的,而这些服务器应该引起一些管理员的注意。

Catch all 可以帮助您的脚本处理任何未必会阻止我们继续进行的意外响应。我们可以选择继续处理,也可以选择提前退出。

Expect Examples摘录:

expect            
    "password:" 
        send "password\r"
     "yes/no)?" 
        send "yes\r"
        set timeout -1
     timeout 
        exit
    # Below is our catch all
     -re . 
        exp_continue
    #
     eof 
        exit
    

【讨论】:

谢谢!多年来我已经找到了解决这个问题的方法(即将整个东西放在一个包装器中),但我会试试这个!我不确定为什么我的 telnet 限制测试可以正常工作,但较大的脚本却不行;不过,正如您所说,很可能我在涉及更多的脚本中的某些会话并没有真正正确关闭(尽管 Expect 似乎是这样认为的)。

以上是关于在期望中终止生成会话的主要内容,如果未能解决你的问题,请参考以下文章

如何在Linux中使用Shell脚本终止用户会话?

终止会话

会话在 64 位应用程序池中短时间后被终止

注销并终止会话

编辑 Summernote 时会话终止

Snowflake UI 会话在 4 小时后不会被终止