使用Expect自动化xterm

时间:2017-12-19 03:51:11

标签: tcl expect xterm

我正在尝试使用xterm自动化Expect窗口(尽管我已经知道Expect无法控制此类GUI应用程序,但在Exploring Expect中解释了一个调整机制

package require Expect 
spawn -pty
stty raw -echo < $spawn_out(slave,name)
regexp ".*(.)(.)" $spawn_out(slave,name) dummy c1 c2
if {[string compare $c1 "/"] == 0} {
    set c1 "0"
}
set xterm_pid [exec xterm -S$c1$c2$spawn_out(slave,fd) &]
close -slave
expect "\n" ;# match and discard X window id

set xterm $spawn_id 

spawn $env(SHELL)

Don Libes提到,从这一点来说,xterm可以自动化,他已经举例说明了如何使用带有interact命令的xterm,

interact -u $xterm "X" {
    send -i $xterm "Press return to go away: "
    set timeout -1
    expect -i $xterm "\r" {
        send -i $xterm "Thanks!\r\n"
        exec kill $xterm_pid
        exit
    }
}

但是,我的期望是发送和期望命令到/从xterm。我试过以下,

send -i $xterm "ls -l\r"; # Prints commands on xterm 
expect -i $xterm "\\\$" ; # Trying to match the prompt

但它没有锻炼。此示例主要依赖于xterm的命令行选项-Sccn

-Sccn

  

此选项允许xterm用作输入和输出通道   现有程序,有时用于专门的应用程序。   选项值指定a名称的最后几个字母   在从机模式下使用的伪终端,加上继承的数量   文件描述符。如果该选项包含&#34; /&#34;划定的角色   用于文件中伪终端名称的字符   描述。否则,从选项中使用正好两个字符   对于伪终端名称,余数是文件描述符。   例子:

-S123/45
-Sab34 
  

请注意,xterm不会关闭任何未打开供自己使用的文件描述符。这是可能的(虽然可能不是   便携式)具有传递打开文件描述符的应用程序   到初始化的xterm或进程的-S选项   在xterm中运行。

我在哪里弄错了?

2 个答案:

答案 0 :(得分:0)

在这里,我从我使用的代码中获取了一个视图。它是从复杂的部分中提取的。

# create pty for xterm
set spawn(PTTY,PID) [spawn -noecho -pty]
set spawn(PTTY,DEVICE) $spawn_out(slave,name)
set spawn(PTTY) $spawn_id
stty raw -echo < $spawn(PTTY,DEVICE)
regexp ".*(.)(.)" $spawn_out(slave,name) dummy c1 c2
if {[string compare $c1 "/"] == 0} { set c1 0 }

# Start XTERM (using -into can place the xterm in a TK widget)
set pid(XTERM) [::exec xterm   -S$c1$c2$spawn_out(slave,fd) {*}$addidtionlXtermOptions  &]
close -slave

# Link
set spawn(SHELL,PID)    [spawn -noecho {*}$commandInXterm]
set spawn(SHELL)        $spawn_id
set spawn(SHELL,DEVICE) $spawn_out(slave,name)

# ...
# send a key or string into the xterm
exp_send -raw -i $spawn(SHELL) --  $key
exp_send -raw -i $spawn(SHELL) -- "$str\r

答案 1 :(得分:0)

Mr.Thomas Dickey指出here时,我开始探索multixterm 最后能够创建一个独立版本,命令直接发送到xterm

主要是我在代码中遗漏的部分是expect_background,它实际上在后台进行链接。希望对所有想要自动化xterm的人都有所帮助。托马斯·迪基先生和丹利比先生的所有学分!

#!/usr/bin/tclsh
package require Expect
set ::xtermStarted 0
set xtermCmd      $env(SHELL)
set xtermArgs     ""

# set up verbose mechanism early
set verbose 0
proc verbose {msg} {
    if {$::verbose} {
    if {[info level] > 1} {
        set proc [lindex [info level -1] 0]
    } else {
        set proc main
    }
        puts "$proc: $msg"
    }
}
# ::xtermSid is an array of xterm spawn ids indexed by process spawn ids.
# ::xtermPid is an array of xterm pids indexed by process spawn id.

######################################################################
# create an xterm and establish connections
######################################################################

proc xtermStart {cmd name} {
    verbose "starting new xterm running $cmd with name $name"
    ######################################################################
    # create pty for xterm
    ######################################################################
    set pid [spawn -noecho -pty]
    verbose "spawn -pty: pid = $pid, spawn_id = $spawn_id"
    set ::sidXterm $spawn_id
    stty raw -echo < $spawn_out(slave,name)
    regexp ".*(.)(.)" $spawn_out(slave,name) dummy c1 c2
    if {[string compare $c1 "/"] == 0} {
        set c1 0
    }
    ######################################################################
    # start new xterm
    ######################################################################
    set xtermpid [eval exec xterm -name dinesh -S$c1$c2$spawn_out(slave,fd) $::xtermArgs &]
    verbose "xterm: pid = $xtermpid"
    close -slave

    # xterm first sends back window id, save in environment so it can be
    # passed on to the new process
    log_user 0
    expect {
        eof {wait;return}
        -re (.*)\n {
            # convert hex to decimal
            # note quotes must be used here to avoid diagnostic from expr
            set ::env(WINDOWID) [expr "0x$expect_out(1,string)"]
        }
    }

    ######################################################################
    # start new process
    ######################################################################
    set pid [eval spawn -noecho $cmd]
    verbose "$cmd: pid = $pid, spawn_id = $spawn_id"
    set ::sidCmd $spawn_id

    ######################################################################
    # link everything back to spawn id of new process
    ######################################################################
   set ::xtermSid($::sidCmd) $::sidXterm
   set ::xtermPid($::sidCmd) $xtermpid

    ######################################################################
    # connect proc output to xterm output
    # connect xterm input to proc input
    ######################################################################
    expect_background {
        -i $::sidCmd
        -re ".+" {
            if {!$::xtermStarted} {set ::xtermStarted 1}
            sendTo $::sidXterm
        }
        eof [list xtermKill $::sidCmd]
        -i $::sidXterm
        -re ".+" {
            if {!$::xtermStarted} {set ::xtermStarted 1}
            sendTo $::sidCmd
        }
        eof [list xtermKill $::sidCmd]
    }
    vwait ::xtermStarted
}


######################################################################
# connect main window keystrokes to all xterms
######################################################################
proc xtermSend {A} {
    exp_send -raw -i $::sidCmd -- $A
}

proc sendTo {to} {
    exp_send -raw -i $to -- $::expect_out(buffer)
}


######################################################################
# clean up an individual process death or xterm death
######################################################################
proc xtermKill {s} {
    verbose "killing xterm $s"

    if {![info exists ::xtermPid($s)]} {
        verbose "too late, already dead"
        return
    }

    catch {exec /bin/kill -9 $::xtermPid($s)}
    unset ::xtermPid($s)

    # remove sid from activeList
    verbose "removing $s from active array"
    catch {unset ::activeArray($s)}

    verbose "removing from background handler $s"
    catch {expect_background -i $s}
    verbose "removing from background handler $::xtermSid($s)"
    catch {expect_background -i $::xtermSid($s)}
    verbose "closing proc"
    catch {close -i $s}
    verbose "closing xterm"
    catch {close -i $::xtermSid($s)}
    verbose "waiting on proc"
    wait -i $s
    wait -i $::xtermSid($s)
    verbose "done waiting"
    unset ::xtermSid($s)
    set ::forever NO
}

######################################################################
# create windows
######################################################################
# xtermKillAll is not intended to be user-callable.  It just kills
# the processes and that's it. A user-callable version would update
# the data structures, close the channels, etc.

proc xtermKillAll {} {
    foreach sid [array names ::xtermPid] {
        exec /bin/kill -9 $::xtermPid($sid)
    }
}

rename exit _exit
proc exit {{x 0}} {xtermKillAll;_exit $x}


xtermStart $xtermCmd $xtermCmd
xtermSend "ls -l\r"
xtermSend "pwd\r"
vwait ::forever