Автоматизация xterm с использованием Expect
Я пытаюсь автоматизировать xterm
использование окна Expect
(хотя я уже знал Expect
не могу управлять такими приложениями с графическим интерфейсом, но в 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)
Дон Либес отметил, что с этого момента xterm может быть автоматизирован, и он привел пример использования xterm с interact
команда следующим образом,
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 в качестве входного и выходного канала для существующей программы и иногда используется в специализированных приложениях. Значение параметра указывает несколько последних букв имени псевдотерминала, используемого в подчиненном режиме, плюс номер унаследованного дескриптора файла. Если параметр содержит символ "/", он отделяет символы, используемые для имени псевдотерминала, от дескриптора файла. В противном случае, из опции для псевдотерминального имени используется ровно два символа, остальное - дескриптор файла. Примеры:
-S123/45
-Sab34
Обратите внимание, что xterm не закрывает дескриптор файла, который он не открыл для собственного использования. Возможно (хотя, возможно, и не переносимо) приложение, которое передает дескриптор открытого файла в xterm после инициализации или опцию -S процессу, запущенному в xterm.
Где я делаю ошибку?
2 ответа
Как указал здесь Thomas Dickey, я начал изучать 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
Здесь у меня есть вид из моего кода, который я использовал. Это извлечено из сложной части.
# 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