Expect - does not force input to spawned process - tcl

package require Expect
#### Log File Name #####
set logFile "mtte_result.log"
set NextLine "\n"
set RTSPrompt "RTS_Mon-> "
exp_spawn plink.exe -telnet -P 10009 10.245.97.42
set spid $spawn_id
#exp_send $NextLine
flush stdout
##### Log the session to the File #########
exp_log_file -noappend $logFile
exp_sleep 5
exp_send $NextLine
expect $RTSPrompt
exp_send "hello world\r"
expect $RTSPrompt
exp_sleep 5
exp_close -i $spid
########################################################
########################################################
Here logfile has output as:
RTS_Mon->
*RTS_Mon->
#####################################
So it seems as if input is not forced to the process.
Is there something wrong to above script?

I don't known if it was a copy&paste error, but I don't think your code will work, should be something like:
package require Expect
# Log File Name
set logFile "mtte_result.log"
set NextLine "\n"
set RTSPrompt "RTS_Mon-> "
exp_spawn plink.exe -telnet -P 10009 10.245.97.42
set spid $spawn_id
exp_send $NextLine
flush stdout
# Log the session to the File
exp_log_file -noappend $logFile
exp_sleep 5
exp_send $NextLine
expect $RTSPrompt
exp_send "hello world\r"
expect $RTSPrompt
exp_sleep 5
exp_close -i $spid
Also make sure that the "exp_spawn" returns properly

Related

Automating xterm using Expect

I am trying to automate xterm window using Expect (though I already knew Expect cant control such GUI applications, but there is a tweaked mechanism explained in 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 mentioned that from this point, xterm can be automated and he has given example to use xterm with interact command as follows,
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
}
}
But, my expectation is send and expect commands to/from xterm. I have tried the following,
send -i $xterm "ls -l\r"; # Prints commands on xterm
expect -i $xterm "\\\$" ; # Trying to match the prompt
But it didn't workout. This example mainly relies on the xterm's command line option -Sccn.
-Sccn
This option allows xterm to be used as an input and output channel for
an existing program and is sometimes used in specialized applications.
The option value specifies the last few letters of the name of a
pseudo-terminal to use in slave mode, plus the number of the inherited
file descriptor. If the option contains a "/" character, that delimits
the characters used for the pseudo-terminal name from the file
descriptor. Otherwise, exactly two characters are used from the option
for the pseudo-terminal name, the remainder is the file descriptor.
Examples:
-S123/45
-Sab34
Note that xterm does not close any file descriptor which it did not open for its own use. It is possible (though probably not
portable) to have an application which passes an open file descriptor
down to xterm past the initialization or the -S option to a process
running in the xterm.
Where am I making the mistake ?
Here I have you a view from my code I used. It is extracted from a complex part.
# 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
As Mr.Thomas Dickey pointed out here, I started exploring on the multixterm
and finally able to make a standalone version where the commands are sent to xterm directly.
The part which mainly I have missed in my code is expect_background which actually does the linking in the background. Hope it helps to all those who all wanted to automate the xterm. All credits to Mr.Thomas Dickey and Mr.Don Libes!!!
#!/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

[Expect]spawn id exp7 not open

I am trying to get my expect script to read a file, run a command for every line of the file and exit saving the log. The code is below:
#!/usr/bin/expect -f
set user [lrange $argv 0 0]
set password [lrange $argv 1 1]
set ipaddr [lrange $argv 2 2]
set arg1 [lrange $argv 3 3]
set systemTime [clock seconds]
set time [clock format $systemTime -format %a_%b_%d_%Y#%H'%M'%S]
set fp [open "$arg1" r]
set a "ssh-"
set b ".txt"
set s "_"
append newfile "${a}${arg1}${s}${time}${b}"
set timeout -1
spawn ssh $user#$ipaddr
match_max 100000
expect "*?assword:*"
send -- "$password\r"
log_file "$newfile" ;
expect "*#"
send_user "This is the $argv0 Script\r"
while {[gets $fp line] != -1} {
send -- "scm $line\r"
expect "*#"
}
close
send -- "exit\r"
expect eof
My problem is that once it comes to the end of the file i get the following error:
E6000_Lab_1# send: spawn id exp7 not open
while executing
"send -- "exit\r""
(file "filetest.tcl" line 28)
Can anyone help me get rid of this error please?
Sorry for doing this but it seems that I got the asnwer myself once again.
Thanks very much for all of you who answer and provide some ideas to the resolution of these issues.
The solution to my problem was on the id of the file that had been opened. Once I closed that, my code stopped crashing, the snipet is below:
while {[gets $fp line] != -1} {
send -- "scm $line\r"
expect "*#"
}
close $fp
send "ping xxx.xxx.xxx.xxx timeout 1 repeat-count 100\r"
expect "# "
send -- "exit\r"
expect eof
As you can see, the "$fp" after the "close" argument allows me to send the next command out of the loop and without errors.
You can't do either send or expect after you close the connection to the subprocess.

eof not recognized in Expect Script

i have got some problems in my expect script.
I am spawning a scp file upload but the eof is not recognized.
The expect rans into the timeout before the script goes on.
What am i doing wrong??
Here is the script
function ssh_upload_file () {
test_log INFO "Uploading File $3 to Device $2 with a $1 seconds timeout" "ssh_upload_file"
last_log_entry_to_detail
expect <<- DONE
set outfilename "$DETAIL_LOG"
log_file "$DETAIL_LOG";
set timeout $1
set lantime_ip $2
system "touch ~/.ssh/known_hosts"
system "ssh-keygen -R $2"
spawn /bin/bash
sleep 3
send_user "Uploading File via SSH...\n"
send "scp $2 $3:$4 || echo FWTEST_SSH_RESULT=\$?\n"
expect "assword:" {
after 1000
send "$DUT_PWD\r"
#set timeout 5
#expect "y/n"
#send "n\n"
#set timeout $1
#expect "#"
#send "no_shell_timeout\n"
}
expect eof
send_user "Done."
log_file
exit 0
expect eof
DONE
LAST_ERROR=$?
return $LAST_ERROR
}
You spawn a bash shell, and send an scp command. When scp completes, you're sitting at a bash prompt. You need to send bash an exit command before you'll see eof.
Alternatively, you're not doing anything in that bash session except capture the output of the scp command which you can do like this:
spawn scp $2 $3:$4
expect "assword:"
send "$DUT_PWD\r"
expect eof
set info [wait]
puts "FWTEST_SSH_RESULT=[lindex [set info] 3]"
Addressing Donal's comment:
spawn scp $2 $3:$4
expect {
"assword:" {
send "$DUT_PWD\r"
exp_continue
}
eof
}
set info [wait]
puts "FWTEST_SSH_RESULT=[lindex [set info] 3]"

Expect is not behaving as expected when I send a command that is too long to fit on a single line

I am trying to create an expect script which will grep a file and return the line which contains the string I am looking for, in this case the string will be a terminal ID. For example I have a file called terminal_list.txt with the following contents:
0x400 192.168.62.133 10006
0x420 192.168.62.133 10021
0x440 192.168.62.133 10022
and I want the line returned which starts with 0x420
My code is as follows:
#!/usr/bin/expect -f
set terminal_list "/home/linkway/terminal_list.txt"
set termid "0x400"
spawn /bin/bash
expect "] "
# Get ip and port of terminal
# Check if termid exists in terminal_list file
set command "grep -q '$termid' '$terminal_list' && echo 'true' || echo 'false'"
send "$command\r"
expect "$command\r\n"
expect -re "(.*)\r\n.*] "
set val $expect_out(1,string)
# If terminal_list does not exist print error and exit
if { [string first "No such file or directory" $val] >= 0 } {
puts "$terminal_list does not exist. Script Failed"
exit
# If terminal_list does not contain termid print error and continue
} elseif { [string match "false" $val] } {
puts "\nTerminal $termid does not exist in ${terminal_list}. Cannot update bootfile.\n"
# If termid is found in terminal_list, get the ip and port of the terminal
} else {
set command "grep '$termid' '$terminal_list'"
send "$command\r"
expect "$command\r\n"
expect -re "(.*)\r\n.*] "
set val $expect_out(1,string)
set ip_port [string range $val 6 end]
}
This works perfectly when I ssh to the RHEL server via putty and run the script in a maximized putty window. HOWEVER when I shrink the window length so that the grep command no longer fits on a single line my code breaks! Can anyone please help me come up with a solution to this? I have been struggling with the processing of expect_out and could really use some guidance.
EDIT: I found what was causing the bug. It turns out that when the grep command is split over multiple lines, a \r is added to the command where the line break is. Here is some debugging info from exp_internal 1. You can see how the \r is added into the grep command where the command ran onto the next line:
expect: does "grep -q '0x400' '/home/linkway/term \rinal_list.txt'
&& echo 'true' || echo 'false'\r\n" (spawn_id exp6)
match glob pattern "grep -q '0x400' '/home/linkway/terminal_list.txt'
&& echo 'true' || echo 'false'\r\n"? no
Why is this happening and what is the proper way to get just the output of the grep command? I find it very strange that expect would behave differently base on how the output of a command is displayed on screen. Any help is greatly appreciated.
I was able to find a cleaner solution to my problem by making my script more expect-like. Here's what it looks like:
set command "grep -q '$termid' '$terminal_list' && echo 'true' || echo 'false'"
send "$command\r"
expect {
-re ".*\r\ntrue\r\n.*] " {
send "grep '$termid' '$terminal_list'\r"
expect ".*\r\n"
expect -re "(.*)\r\n.*] "
set val $expect_out(1,string)
set ip_port [string range $val 6 end]
puts "\nUpdating $termid bootfile"
updatebootfile $ip_port $boot_data $termid
}
-re ".*\r\nfalse\r\n.*] " {
puts "\nTerminal $termid does not exist in ${terminal_list}. Cannot update bootfile.\n"
}
-re "No such file or directory.*] " {
puts "$terminal_list does not exist. Script Failed"
exit 1
}
timeout {
puts "expect timeout when searching for $termid in $terminal_list"
}
}

get the result of remote command in Expect

I have Expect script invoked from a bash script. The Expect script spawns
a ssh session and runs a remote command. I want to have a return code
of the remote command available in my shell script.
# !/bin/sh
expect -f remote.exp 192.168.1.100 user pass 10.10.10.1
# here I want to analyze exit status of remotely invoked command
The expect script is below, here is my ugly workaround:
#!/usr/bin/expect
set timeout 20
set box [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
set target [lindex $argv 3]
spawn ssh -l $user $box
expect "*password:"
send "$password\r\n"
expect "*#"
send "ping -c1 $target\r\n"
expect "*#"
send "echo $?\r"
expect {
"0\r" { puts "Test passed."; }
timeout { puts "Test failed."; }
}
expect "*#"
PS. I read that expect_out may help me, but I failed to get it work. Any help
will be greatly appreciated! Thanks.
I have tried the following and it works on a Mac:
#!/usr/bin/expect
# Process command parameters
foreach {host user password target} $argv {break}
set command "ping -c 1 $target"
# Log in
spawn ssh -l $user $host
expect -nocase "password:"
send "$password\r"
# Send the command
expect "$ "
send "$command\r"
# Echo the return code, the key here is to use the format code=X instead
# just a single digit. This way, Expect can find it
expect "$ "
send "echo code=$?\r"
# Analyze the result
set testResult "Test failed."
expect {
"code=0" { set testResult "Test passed." }
}
puts "$testResult"
# Done, exit
expect "$ "
send exit
Note a few differences:
The shell prompt on my system is "$ "
I only send \r, not \r\n. I don't know if it matters
I exit the ssh session by sending "exit"