免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1350 | 回复: 0
打印 上一主题 下一主题

关于在Linux/Unix环境下的Terminal终端自动交互 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-09-10 13:57 |只看该作者 |倒序浏览

               
               
                如何利用Expect来和Terminal自动交互
在Linux/Unix的世界里,用expect就可以很容易得就可以实现人机交互的功能,尤其网上好多examples实现比如password, ftp, telnet之类的程序都很简单。但是如何很好的实现和终端程序交互,尤其是这个终端是用ncurses/curses实现,有着丰富的颜色和复杂的结构,这样的现有的expect就有点力不从心了。现在,笔者参与一个OpenSolaris的项目测试,这个测试要求能够自动和终端交互,来实现复杂的自动人机交互的功能。但是这个项目使用ncurses + python bridge写成的。为了追求好的用户体验,开发人员使用了很多的屏幕刷新,这就要求expect能购追踪每一次刷新并且从用户的最终角度来得到屏幕显示的是什么。
比如:原来屏幕上显示的是"Yes or no....", 但是在下一屏刷新的时候,可能之改变了几个字符,显示就变成了"Yesterday", 如下所示:
Y e s   o r   n o ...
Y e s t e r d a y ...
但是在expect_out(buffer)里面,显示的就是"...Yes or no\r\n...^[[3Gterday", 这样如果搜寻"Yesterday"这串字符就显然不可能能够搜到的。
有人会问"^[[3G"是什么?这个就是termcap/terminfo 用来使terminal具有丰富显示功能的约定俗成方法。为了方便使用这些功能,人们就把他们组成了各种方法,封装在curses/ncurses库中。具体约定的方法请参见附录1
在expect的创始人Don.Libes的论文〔附录2〕中, 他提出了一种方法能够解决上述terminal问题的自动人机交互,而且作为expect的FAQ给出。但是,他实现的方法是用tcl/tk来模拟一个后台终端(tk terminal),把显示的内容用控制的方法来实现前台终端和后台终端同步显示,在他封装的脚本里,用term_expect〔附录3〕来对后端terminal进行监控从而间接的对用户程序显示的前台进行监控。
但是他的方法里面有一个限制,就是前端运行的terminal程序必须能在后端的tk Terminal上正常显示,但是,据我所知,不同的terminal控制转义字符是不一样的,而tk terminal的转移字符远远不能够覆盖所有其他的terminal类型,比如xterm. 而且有很多程序只能在诸如xterm这样的终端上才能够正常运行。这就造成了他给出的程序在实际应用中受限。更何况很多情况下根本就不允许tk存在(在没有X的情况下)。
Don.Libes 的例子中也给出了virterm〔附录4〕作为非tk terminal的解决方案。但这同样有问题,首先这个程序本身就是一个学生写的,用于从他所在大学的图书馆里自动检索,而且他模拟的后台终端是tt终端现以不多见。所以在把virterm直接用于本例也不能够实现。
但是这里的核心思想却很有用,在后端模仿一个terminal的buffer,来把有用的东西写到buffer里面。寻找一个定位符(cursor),根据expect_out(buffer)的内容不断的调整cursor的位置,不断进行刷新。这样在buffer里面就能够保证和前端terminal显示一致。
首先要根据控制序列的所在终端调整完全转义序列的内容,使之在expect_out(buffer)一个个的得到处理,重点是根据内容来调整cursor.在我的prototype的实验中,如下内容可以用于xterm.
expect {
    -re "^\r" {
        # (cr,) Go to to beginning of line
        set cur_col 0
        term_cursor_changed   
    } "^\n" {
        # (ind,do) Move cursor down one line
        term_down
        term_cursor_changed
    } "^\b" {
        # Backspace nondestructively
        incr cur_col -1
        term_cursor_changed
    } "^\a" {
        # Bell, pass back to user
        send_user "\a"
    } "^\t" {
        # Tab, shouldn't happen
        send_error "got a tab!?"
    } eof {
        term_exit
    } "^\x1b\\\[K" {
        # (ind,do) Move cursor down one line
        term_down
        term_cursor_changed
    } "^\x1b=" {
        #???
    } "^\x1b(B" {
        #
    } "^\x1b(0" {
        #
    } "^\x1b\\\[A" {
        # (cuu1,up) Move cursor up one line
        incr cur_row -1
        term_cursor_changed
    } "^\x1b\\\[C" {
        # (cuf1,nd) Nondestructive space
        incr cur_col
        term_cursor_changed
    } -re "^\x1b\\\[((\[0-9]{1,})(;*)(\[0-9]{1,}))*r" {
        set cur_row [expr $expect_out(2,string)+1]
        set cur_col $expect_out(4,string)
        term_cursor_changed        
    } -re "^\x1b\\\[(\[0-9]{1,2})*G" {
        # (cuu1,up) Move cursor to the line
        set cur_col $expect_out(0,string)
        term_cursor_changed
    } -re "^\x1b\\\[(\[0-9]{1,2})*m" {  # unsupported
    } -re "^\x1b\\\[((\[0-9]{1,2})(;*)(\[0-9]{1,2}))*m" {  # unsupported
    } -re "^\x1b\\\[((\[0-9]{1,2})(;*)(\[0-9]{1,2})(;*)(\[0-9]{1,2}))*m" {  # unsupported
        # (rmso,se) End standout mode
        set term_standout 0
    } -re "^\x1b\\\[(\[0-9]{1,})*X" {
    } -re "^\x1b\\\[(\[0-9]{1,})*H" {
        # (cuu1,up) Move cursor to the line
        set cur_row $expect_out(0,string)
        term_cursor_changed
    } -re "^\x1b\\\[((\[0-9]*);(\[0-9]*))*H" {
        # (cup,cm) Move to row y col x
        set cur_row [expr $expect_out(2,string)+1]
        set cur_col $expect_out(3,string)
        term_cursor_changed
    } -re "^\x1b\\\[(\[0-9]*)*l" {
    } -re "^\x1b\\\[(\[0-9]*)*J" {
        # (clear,cl) Clear screen
        term_init
        term_cursor_changed
    } -re "^\x1b\\\[\\\?(\[0-9]{1,})*h" {
        # (smkx,ks) start keyboard-transmit mode
         # terminfo invokes these when going in/out of graphics mode
        set cur_row 1
        term_cursor_changed
    } -re "^\x1b\\\[\\\?(\[0-9]{1,})*l" {
    } -re "^\[\x20-\x7e]+" {
        term_insert $expect_out(0,string)
        term_cursor_changed
    }
   在项目的具体实现过程中,由于匹配效率的问题,上述方法被另外以 C库来写成。首先我们用expect调取程序来实现终端交互,并且把expect_out(buffer)的内容输出供C程序来调取进行实施分析。后端的buffer由C程序来进行实现。然后expect在根据这个buffer里面的内容能够来对前端程序进行匹配和其他操作。
   整体的结构图如下:
  

附录:1. 控制终端代码 - Linux 控制终端转义和控制序列 http://www.chinalinuxpub.com/bbs/showthread.php?t=45194
2. Automation and Testing of Character-Graphic Programs
3. term_expect: http://expect.nist.gov/example/term_expect
4. virterm: http://expect.nist.gov/example/virterm
               
               
               
               
               
               
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u/412/showart_2049636.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP