Expect 在网络管理中发挥着重要作用

实现所有命令行操作自动化的脚本编写工具

Expect 是进行高效的系统和网络管理工作不可或缺的一种工具,不过很多人对它存在着一定的误解。在本文中,将通过一些常见的用例来介绍 Expect 所提供的优点。

Cameron Laird (claird@phaseit.net), 副总裁, Phaseit, Inc.

Photo of Cameron LairdCameron Laird 是 developerWorks 长期投稿者和前专栏作家。他经常编写关于促进其公司应用程序开发的项目的文章,主要关注可靠性、安全性和最初不是为协作而设计的系统集成。



2007 年 9 月 13 日

如果您从事系统和网络管理工作,那么您将需要 Expect。

更准确地说,您为什么不使用 Expect 呢?对于一些常见的任务,它可以节省大量的时间。尽管您现在可能已经在使用 Expect,但是您可能并不是很清楚下面所描述的一些功能。

Expect 可以实现命令行交互的自动化

要从 Expect 中获益,您并不需要掌握有关它的所有内容;让我们从一个具体的示例开始,研究 Expect 如何在 AIX® 或者其他操作系统中简化您的工作:

假设您在一些 UNIX® 或者类 UNIX 主机上具有登录帐号,并且您需要更改这些帐号的密码,但是并没有使用网络信息服务(Network Information Service,NIS)、轻量级目录访问协议(Lightweight Directory Access Protocol,LDAP)或者能够在每台计算机上识别出您是相同的登录用户的一些其他机制对这些帐号进行同步。在大多数情况下,登录到一台特定的主机,并运行合适的 passwd 命令并不会花费很长的时间,可能只需要一分钟而已。但是因为无法将您的密码编写在脚本中,所以您必须 进行“手动”登录,是这样吗?

其实并不是这样的。事实上,标准 Expect 分发版(完整的分发版)中包括一种命令行工具(以及描述其使用的手册页面!),该工具恰好可以负责完成这项烦琐的工作。passmass(请参见参考资料)是使用 Expect 编写的一个简短的脚本,它可以使得在二十台计算机上进行密码更改的工作就像在一台计算机上进行密码更改那样简单。不需要反复地输入相同的密码,您可以只启动一次 passmass,并允许您的桌面计算机负责更新每个单独的主机。您节省了大量时间并可以稍事休息,同时对于已经输入过的内容,极大地降低了错误输入的可能性。

Expect 的限制

这个 passmass 应用程序是一个非常优秀的模型,它说明了 Expect 的许多常规特性:

  • 这个工具值得我们去使用和研究:这个实用工具已经编写完成,并且可以免费地下载,它易于安装和使用,可以节省大量的时间和精力。
  • 从某种意义而言,它的作用是“无关紧要的”。如果任何操作都“按照既定的规则”进行(如果您使用了 NIS 或者一些其他的域身份验证或单点登录系统),或者可以通过编写脚本进行登录,那么就不需要使用 passmass 了。但实际情况并不总是这样的,而 Expect 非常适合于处理各种各样现有的问题。也许 Expect 能够帮助您节省更多的时间,以便您能够使您的配置更加合理化,这样一来您就不再需要 Expect 了。在此期间,您可以充分地利用它。
  • 对于分布式的环境,passmass 仅使用 telnetrlogin 或者 slogin 进行登录。我希望当前所有的 developerWorks 读者都不再使用这些协议,而是使用 sshpassmasss没有 对 ssh 提供全面的支持。
  • 另一方面,几乎所有与 Expect 有关的内容都编写得非常清楚,并且可以免费获得。只需要使用三行简单的内容(至多)就可以对 passmass 进行增强,以支持 ssh 和其他选项。

您可能已经了解了足够多的内容,完全可以开始编写或者修改您自己的 Expect 工具。当然,实际上 passmass 分发版中包含了以 ssh 方式进行登录的代码,但是省略了相应的命令行解析以到达这部分代码。本文介绍了一种方法,您可以修改分发版源代码,对 sshtelnet 以及其他协议进行同样的处理:

清单 1. 经过修改的、接受 -ssh 参数的 passmass 片段
            ...
         } "-rlogin" {
            set login "rlogin"
            continue
        } "-slogin" {
            set login "slogin"
            continue
        } "-ssh" {
            set login "ssh"
            continue
        } "-telnet" {
            set login "telnet"
            continue
           ...

在我自己的代码中,我实际上从这个“样本”中提出了更多的内容。现在,passmass 第 100 行附近的这一连串测试,非常好地说明了 Expect 的可读性。这里并没有涉及到很深的编程技术,不需要面向对象、单体应用程序、协同例程,或者其他巧妙的技术。您只是请求计算机负责您通常进行的输入工作。恰好,这个简单的操作步骤可以节省大量时间和精力。

什么是 Expect?

Expect 究竟 什么,您应该如何使用它呢?

“Expect”涉及到一些独特的概念,许多经常使用 Expect 的用户对这些概念并不是十分清楚:

  • Expect 是一种特定的、高级的和通用的编程语言,其语法与 Tcl 相同,并增加了 Tcl 中所没有的一些特殊用途的命令。
  • Expect 是一种可执行程序,从它正确地处理用 Expect 语言编写的输入的角度来看,它实现了这种语言。
  • expect 命令是其中的一个命令,Expect 以此对 Tcl 进行了扩展。
  • Expect 是一个 Tcl 包。一般说来,这意味着任何 Tcl 应用程序都可以在运行时加载 Expect 功能。
  • Expect 是一个基于 C 源代码的库,而这些 C 源代码则深入到 Expect 可加载的包和 Expect 可执行程序。
  • Expect 是某种工具的抽象概念,该工具:
    • 实现终端交互的自动化,甚至在涉及到密码或者其他特殊项目的情况下
    • 实现了一种“对话”模型,通过它对消息和响应的简单规律进行编码
    在这种抽象中,不存在特定于 Tcl 的内容,实际上,现在有几种使用其他语言(如 Python、Perl,等等)的 Expect 模型的独立实现。尽管本文中的示例都采用基于 Tcl 的 Expect 进行表述,但是可以使用其他语言来编写所有这些示例。没有理由因为您熟悉或者不熟悉 Tcl,而限制您对 Expect 的使用。

请注意,考虑到一些技术细节超出了本文的关注重点,在前面的描述中,我稍微有些歪曲事实;例如,常规的 Expect 可执行程序不仅扩展了 Tcl 命令集,它还可以识别启动时的一些额外的命令行参数。尽管这些内容并不是专门针对主要的主题:一个简短的 Expect 程序所能够为系统管理员完成的工作,超出了大多数人的预期。

在了解了这个背景信息之后,“passmass 是一个 Expect 应用程序”表示:

  • passmass 是一个以 Expect 语言编写的文本文件。
  • 如果您在正确地安装了 Expect 和 passmass 的主机上执行 expect passmass ...,那么您将得到正确的 passmass 功能。

请注意,具体的执行方法可能有一些变种:可以在一个 Tcl 解释器中以交互的方式加载 Expect、运行 passmass、创建 passmass 作为一个独立的可执行程序,等等。同样地,这些替代方法超出了本文所关注的重点。

网络管理示例

让我们考虑一项更大的挑战,网络操作中心的日常操作中一项更典型的操作:检索一组托管的 Cisco 交换机的当前配置信息。尽管有些站点使用 SNMP 或者 HTTP 来进行这些操作,但是更常见的是使用控制台或者频内 telnet 会话来获取该信息。许多管理员认为,完成这项工作唯一可行的方法是输入与清单 2 中所示类似的命令。

清单 2. 典型的“手工”配置自检
        telnet $MY_ROUTER
        [User: admin]
        [password: ...]
        CCNA01# show running-config
        [... Current configuration:
         ... version 12.0 ...
         FIFTY LINES MORE OF CONFIGURATION DETAIL
         ...
         End]

其实并不是这样的;实际上,通过在 cron、回复邮件(mailback)服务器,或者类似的作业控制机制(请参见清单 3)中进行调用,这项工作完全可以实现自动化。

清单 3. 配置自检的自动化
      #!/usr/bin/expect

      # initialize host, password, ...

      package require Expect
      set prompt {[00m# }
      spawn telnet $host
      expect {User: }
      send admin\r
      expect password:
      send $password\r
      expect -exact $prompt
      send "show running-config\r"
      expect -exact $prompt
      send exit\r

这个脚本可以自动地检索路由器的配置信息,即在正确地编写了脚本之后,对其进行监视时不再需要输入密码或者进行干预。

一些常见的混淆

这个示例是典型的 Expect 使用情况,其中有几点值得注意。首先,不存在任何适用于各种情况的解决方案。许多管理员在获得 Expect 时都认为它是一个单独使用的工具,以解决他们当前所碰到的问题。那不是 Expect。Expect 与电子铁钉寻找器 (electronic stud finder) 不同,后者可以不依靠任何其他工具、独立地查找铁钉。Expect 更像是一个手钻:您必须将其与合适的钻头,或者转换接头、孔锯,或者其他附件进行组合,才能真正实现它的用途。

同样地,Expect 至少需要进行一些自定义工作。当然,我通常使用 Expect 来解决一些其他工具所无法轻松解决的问题。running-config 的案例说明了,只需要几行内容,Expect 就可以自动地获得大量的信息。即使是这个简单的示例,也呈现出 Expect 所带来的一些问题。例如,这种特定的自动化,需要将管理密码以明文的形式嵌入到 Expect 脚本中;您需要判断,它是否适合于您的具体环境。

这个案例至少在一些更多的方面是非常典型的。$prompt 让我感到有些奇怪。屏幕上所显示的是 CCNA01# ;对于许多设备来说,这是非常典型的,这个提示符实际上嵌入了一些不可见的控制字符。幸运的是,Expect 提供了各种有价值的调试开关,以报告其交互过程;这正是我确定 CCNA01 所生成的各种字符的方法。

另外,如前所述,清单 3 返回了整个会话,包括我所需要的配置报告,还加上登录和退出作为其开始和结束。隐含在一般请求中的另一个有关 Expect 的常见误解是“检索一个命令的值”。Expect 并没有提供这些语义。当您坐在键盘前并输入一个命令时,您将它的操作认为是在下一个提示符之前所看到的相关显示。对于网络互连协议(如 telnet),在结果和提示符之间实际上并没有什么区别;您可以作为一个观察者对这些内容的含义进行分析。

与这种网络通信模型完全一致,Expect 并不直接区分结果和提示符。所有的 Expect 都称为对话,即它通过 send 发送的字符序列,以及它所期望(expect)的内容。因此在实际中,我使用一个正则表达式解析来为我提供所需要的细节信息。Expect 的扩展正则表达式功能非常强大,并且很容易通过代码确定格式。现在,让我们重点关注通常无法通过脚本实现自动化的广泛主题。

并发

请记住,Expect 是一种功能强大的、通用的语言。实际的 Expect 应用程序通常用于解析命令行参数、将结果显示在分栏的表格中、从数据库中检索历史数据、显示图形用户界面 (GUI),以及更多的用途。所有这些都是常规的计算工作,本文中的示例并没有展示这些方面的内容。文本的重点是正确地理解 Expect 独特的价值。

使用与清单 3 中所示类似的解决方案,网络管理员通常会考虑下一步的“水平方向的”增强:从大量类似的计算机检索这类报告。Expect 提供了循环构造 foreachwhile 等等,从而使得这样的工作变得更加简单。

它也可能很快地变得无法接受。假设您负责上百台 LAN 主机,这是一个比较常见的情况。您使用一个 Expect 脚本自动地依次登录到这些计算机,并检索重要的数据。现在,您对该脚本稍微进行一下抽象,以便对整个集合进行遍历。

问题是,运行所得到的脚本可能会花费很长的时间。它登录到一台主机,请求结果,接收结果,注销,然后转向下一台主机,请求一个新的结果,等等。这个过程中的延迟使得人们希望能够使用某种方法一次性地请求所有的结果,并按照结果到达的顺序对其进行收集,导致这些结果不同顺序的原因包括网络滞后、不同的负载,以及其他延迟。

有一种方法可以实现这种操作。实际上,Expect 提供了一些非常好的功能来同时管理多个对话。本文提供了一个程序的示例,该程序多次进行登录,在每个登录上执行一些长时间运行的命令,然后根据结果到达的顺序进行接收;对这个案例进行了整理,以便这些结果返回的顺序与其启动顺序相反(请参见清单 4)。

清单 4. 并发检索来自多个登录的报告
       #!/home/claird/local/ActiveTcl/bin/tclsh 
       
       package require Expect
       
       log_user 0
       
       # Initialize user, passphrase, ... here.
       
           # Sequentially login and issue time-consuming commands to all
           # hosts.
       for {set i 0; set delay 8} {$delay > 0} {incr i; incr delay -1} {
           spawn ssh $user@$host
           set sid($i) $spawn_id
           expect rsa':
           send $passphrase
           expect "Last login"
           expect -ex $prompt
           set active($sid($i)) $i
           send -i $sid($i) "echo `date`; sleep $delay; echo `date`; echo \
           '$delay done on $i.'\r"
       }
       
       while {[llength [array names active]]} {
           expect -i [array names active] -ex $prompt {
                puts "RECEIVED:  $::expect_out(buffer)"
                send -i $expect_out(spawn_id) exit\r
                expect -i $expect_out(spawn_id) eof
                unset active($expect_out(spawn_id))
           }
       }

当您运行这个脚本时,将看到与清单 5 所示类似的结果。

清单 5. 运行清单 4 所得到的结果
       RECEIVED:  echo `date`; sleep 1; echo `date`; echo '1 done on 7.'
       Mon Apr 23 22:15:15 UTC 2007
       Mon Apr 23 22:15:16 UTC 2007
       1 done on 7.
       RECEIVED:  echo `date`; sleep 2; echo `date`; echo '2 done on 6.'
       Mon Apr 23 22:15:15 UTC 2007
       Mon Apr 23 22:15:17 UTC 2007
       2 done on 6.
       RECEIVED:  echo `date`; sleep 3; echo `date`; echo '3 done on 5.'
       Mon Apr 23 22:15:15 UTC 2007
       Mon Apr 23 22:15:18 UTC 2007
       3 done on 5.
          ...
       RECEIVED:  echo `date`; sleep 8; echo `date`; echo '8 done on 0.'
       Mon Apr 23 22:15:14 UTC 2007
       Mon Apr 23 22:15:22 UTC 2007
       8 done on 0.

在编程的层次上,请注意,所有这些登录都是通过基于密码的 ssh 登录到相同的主机,并使用相同的用户和密码凭据。在一个更实际的示例中,可以通过更多行的代码来管理各个不同的主机,对每个主机使用不同的帐号和登录协议。尽管对于那些对网络管理员非常 价值的命令,sleep 是一个很好的模型;但是没有理由先睡眠数秒钟,然后再返回。

即使这个示例,也无法尽述 Expect 的所有功能。Expect 还提供了使用一个内置的套接字编程接口,直接管理 TCP/IP 对话的功能,并且它可以实现半自动化,在两种不同的模式之间来回切换,用户在其中一种模式中输入部分对话,而 Expect 在另一种模式中实现所有操作的自动化。

我们将在以后的文章中介绍这些主题。本文的目标是,展示 Expect 中包含了大多数管理员并不知道的许多内容,具体来说是了解了该工具后,马上就能够解决网络管理工作中各种常见的问题。在脚本中使用密码和密码条目,以及并发地控制多个连接,这些功能都是非常强大的。

总结

Expect 可以完成所有看起来无法实现自动化的工作:在脚本中使用密码条目、登录到远程用户的会话和返回对他或者她的控制,以及更多的工作。尽管它已经得到了广泛使用,但是 Expect 却常常被人们所误解。正确地理解有关 Expect 的一些基本知识(如何调用它、它的对话模型、它的编程辅助,等等),以便在系统和网络管理工作中更充分地发挥它的作用。

参考资料

学习

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文
  • Expect:Expect 主页采用了老式的风格。在很大程度上,Expect 的功能与 15 年前的首个发行版的功能是相同的,并且它的 Web 站点更倾向于迎合那些关注长期价值而不是堂皇外表的读者。
  • ActiveTcl:许多 UNIX 在分发时都提供了 Expect,但通常这些二进制文件都不是最新的。对于大多数情况,更新 Expect 的最简单的方法就是安装 ActiveTcl。ActiveTcl 包括所有的 Expect 运行时功能,并且它可用于 Windows®、Mac OS X、Linux®、Solaris、AIX 和 HP-UX。
  • Exploring Expect:到目前为止,所发行的、重点关注 Expect 的图书仅此一本。这本书编写得非常好,以至于经过了二十多年都没有进行更改,根据出版社的统计,该书每年仍然可以销售数千本。这一成就体现了 Expect 的特征:这本书的首次发行是在 1994 年,尽管与那时的版本相比,现在 Expect 的功能要强大得多,但是其基本思想 却仍然没有改变,并且适用于 2007 年的计算工作。Don Libes 编写了 ExpectExploring Expect,并且继续在对前者进行不断完善。这本书提供了许多有价值的细节,它们要比本文中的内容更加深入。从某种程度上来说,本文的目的是向那些拥有该书的读者说明,有更多强大的功能可供使用。
  • passmass手册页面,以及用于最新的 Expect 的所有其他示例工具和文档标准,可以在线获取。
  • UNIX 生产力技巧”:这篇文章推荐使用 Expect 实现命令行交互自动化,以及使用许多其他有价值的命令行秘诀来提高工作效率。为什么与 Stutz 相比,我用了十倍的篇幅却得到了相同的结论呢?这是因为我发现,当新的用户刚刚接触 Expect 时,对其存在许多的误解。Expect 的功能非常丰富,全面地了解这些内容是非常重要的,我的目的就是帮助实现这个目标。
  • 这个关于 Expect 的 Wiki 页面提供了一些有趣的注释,其中包括如何保护 Expect 脚本、如何检索单个命令的结果,等等方面的细节。
  • Expect 超出预期”:这篇关于 Expect 的介绍着重于它的广泛性,也就是,它用于各种编程需求解决方案的表达能力。
  • Android:许多 Expect 的新手非常想知道,该工具是否能够像灵活地处理命令行对话那样实现 GUI 交互的自动化。它不能。GUI 应用程序级别的脚本编写是一个棘手的问题,超出了本文的范围。在解决这个问题的许多项目之中,“Android”是一个有趣的 GUI 应用程序测试框架。Android 和 Expect 之间存在一定的联系。
  • Tcl:Expect 扩展 Tcl:它是 Tcl,但是加上了一些用于终端管理、对话解析和调试的额外工具。“什么是 Tcl?”Tcl 是一种高层次的、通用的语言,它是轻量级的、可移植的,并且学习起来非常容易。这意味着,通常在使用 Tcl 的地方都可以使用 Expect(特别是在所有的 UNIX 和 Windows 中),Expect 应用程序可以重用 Tcl 编程,并且自动地在 Expect 中呈现这些 Tcl 功能。
  • Debugging Expect programs:这个页面突出介绍了特定于 Expect 的一些关键技术(主要是 autoexpectexp_internalinteract)。
  • Automating the Management of Network Devices through the Command Line:这篇文章着重于那些不使用 Expect 就无法完成,或者至少不现实的任务,比如在脚本中使用密码条目,等等。这种说法并不是很严格,还可以使用 Perl、bash 和一些其他语言来插入延迟的动作以实现有关密码的自动化。时间延迟是一种内在的缺陷,Expect 需要避免这种情况。通过时间延迟的方式进行编程,毫无疑问会引入一些困难,从而使维护工作变得更加复杂。这篇文章详尽地介绍了该技术及其与 Expect 风格之间的对比。
  • 对话 UNIX,第 9 部分:正则表达式”:要最大程度地利用 Expect,其中所需的部分工作就是合理地使用正则表达式。这篇文章对正则表达式进行了很好的介绍。另外,我维护了一个站点,其中包含一些与正则表达式相关的问题。
  • 查看 Cameron Laird 撰写的其他文章和教程:
  • 受欢迎的内容:查看您的同事所感兴趣的 AIX 和 UNIX 文章。
  • AIX and UNIX 专区:developerWorks 的“AIX and UNIX 专区”提供了大量与 AIX 系统管理的所有方面相关的信息,您可以利用它们来扩展自己的 UNIX 技能。
  • AIX and UNIX 新手入门:访问“AIX and UNIX 新手入门”页面可了解更多关于 AIX 和 UNIX 的内容。
  • AIX 5L™ Wiki:发现 AIX 相关技术信息的协作环境。
  • 按主题搜索“AIX and UNIX”库:
  • Safari 书店:访问此电子参考资料库可查找特定的技术资源。
  • developerWorks 技术事件和网络广播:了解最新的 developerWorks 技术事件和网络广播。
  • Podcast:收听 Podcast 并与 IBM 技术专家保持同步。

获得产品和技术

  • IBM 试用软件:从 developerWorks 可直接下载这些试用软件,您可以利用它们开发您的下一个项目。

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=AIX and UNIX
ArticleID=254867
ArticleTitle=Expect 在网络管理中发挥着重要作用
publish-date=09132007