How to store output in a variable while using expect 'send' command - tcl

Thanks.
But the account and password are needed. So I must send them and then send ovs-vsctl command.
the scripts are something like this:
spawn telnet#ip
expect -re "*login*" {
send "root"
}
expect -re "password*" {
send "****"
}
send "ovs-vsctl *******"
I want to store the output of this command send "ovs-vsctl ****", but many times I got some output of the command "send "password"", how can I get the output of send "ovs-vsctl****". The output of the command send "ovs-vsctl *** are two string and each string occupies one line.
Thanks.

Maybe:
log_user 0 ;# turn off the usual output
spawn telnet#ip
expect -re "*login*"
send "root\r"
expect -re "password*"
send "****\r"
send "ovs-vsctl *******"
expect eof
puts $expect_out(buffer) ;# print the results of the command

Expect works with an input buffer, which contains everything that is returned from the interactive application, meaning both process output and your input (as long is it is echoed from the remote device, which is usually the case).
The expect command is used to recover text from the input buffer. Each time a match is found, the buffer up to the end of that match is cleared, and saved to $expect_out(buffer). The actual match is saved to $expect_out(0,string). The buffer then resets.
What you need to do in your case is match the output with an expect statement, to get what you want.
What I would do in your case is match the remote device prompt after sending the password, then match it again after command was sent. That way the buffer after the last match will hold the needed output.
Something along the lines of:
[...]
expect -re "password*" {
send "****"
}
expect -re ">"
send "ovs-vsctl *******\r"
expect -re ">" # Better if you can use a regexp based on your knowledge of device output here - see below
puts $expect_out(buffer)
By matching using a regexp based on your knowledge of the output, you should be able to extract only the command output and not the echoed command itself. Or you can always do that after-the-fact, by using the regexp command.
Hope that helps!

Related

Different output when a tcl script is launched via tclsh and wish interpreters

I wrote a Tcl script to telnet a Linux server and perform some operations in it.
I send some command and expect * (expected everything to be captured in buffer).
Now I print the $expect_out(buffer) in a file.
Note: the command I send to the linux server invokes some sort of application in Linux (like a top command), but the output of that command is always the same if executed any number of times.
Double clicking the script file or wish script.tcl (using wish interpreter)
After completing the execution, I open the file and check for its contents. It has the contents of previous expect, i.e., a previous expect before expect * in the script, and some part of this expect (expect *). So the file content misses some text at the end.
Running the script from command prompt (using tclsh interpreter)
#> tclsh script.tcl
After completing the execution, I open the file and check for its contents. It has the contents of previous expect, i.e., a previous expect before expect * in script, and a all part of this expect (expect *).
In the above cases, if expect * is replaced with some text like -> expect "hello", then the script fails in wish interpreter and suceeds in tclsh interpreter.
Why is that? How can I fix it?
The script is as below
package require Expect
set DOWN2 \033OB
spawn telnet 172.19.10.10
after 30000
expect "login:"
send "admin\r"
expect "Password:"
after 1000
send "welcome123\r"
after 2000
expect {
timeout { exit 1 }
"MAIN"
}
expect ">"
after 3000
send "mgmt\r"
expect {
timeout { send_user "timed out, Bye "; exit 1 }
"Protocol"
}
#set fp [open textdoc.txt a+]
send "translation\r"
expect "prefix"
send "\r\r"
send "$DOWN2"
send "$DOWN2"
send "$DOWN2"
send "$DOWN2"
send "326"
send "\x16"
expect -re {limit No. : (\d+)}
set lim_val $expect_out(1,string)
set exp_out $expect_out(buffer)
set fp [open textdoc.txt a+]
puts $fp "total buffer is $exp_out"
close $fp
after 2000
exit
This code is successful with tclsh and fails for wish interpreter
fails at line >> set lim_val $expect_out(1,string)
But from the wireshark capture, I have found out that the text "limit No. : 2" is received even if run from wish.
If I try to print $expect_out(buffer) in file,
In tclsh, the contents stored in buffer is till the "limit No. : 2" is occurred
In wish, the contents stored in buffer ends even before the text "limit No. : 2" is occurred.

expect: how to send an EOF to spawnd process

I have a program read from stdin and process it. ( like "tee /some/file" )
This program wait stdin end to exit itself.
If I spawn it from Expect, after I send many content, how to send an "EOF" to the program?
there is a close command in Expect, but it will also send a SIGHUP, and can not expect program output anymore.
Expect works (on non-Windows) by using a virtual terminal which the spawned program runs within. This means that you can do things by sending character sequences to simulate keys. In particular, the EOF control sequence is done with Ctrl+D, which becomes the character U+000004. The terminal processes this to turn it into a true EOF.
There's a few ways to write it, depending on which escape sequence you prefer, but one of these will work:
# Hexadecimal-encoded escape
send \x04
# Octal-encoded escape
send \004
# UNICODE escape (also hexadecimal)
send \u0004
# Generate by a command
send [format "%c" 4]
When Expect is using Tcl 8.6, these all generate the same bytecode so use whichever you prefer.

Expect programming:How to expect a particular string

I have logged in the CUCM then we are trying to execute one command which is coming correct, but by the Expected value we are not able to execute the second command.
first we are doing like this:-
send -i $install_id "utils ctl set-cluster mixed-mode\r\n"
puts "we are hitting yes &&&&&&&&&&"
expect {
-i $install_id -re ".*" {
send -i $install_id "y\r"
puts "$$$$$$$$$$$$$$$ we are inside...."
}
}
puts "we are done %%%%%%%%%%"
return 1
here first command send -i $install_id "utils ctl set-cluster mixed-mode\r\n" is getting executed sucessfully , but it is not going inside the expect loop.
the output of first command is something like:-
admin:utils ctl set-cluster mixed-mode
This operation will set the cluster to Mixed mode. Do you want to continue? (y/n):
after this the cursor will be in next line where i have to give y and enter, the output statment inside expect is also not getting printed
I'd be doing something like:
expect {
-i $install_id
-ex "continue? (y/n):" {
puts "I FOUND A CONTINUE PROMPT"
send -i $install_id "y\r"
exp_continue
}
"$thePrompt" {
puts "I FOUND A SYSTEM PROMPT"
}
}
Like that, it would only stop that expect once the command has finished, but it will respond to the continue prompt and keep going (because exp_continue). In this case, the -ex option is suitable as that matches a literal string, a great convenience when the thing that you are looking for contains multiple regular expression metacharacters (?, ( and )).
And change "$thePrompt" for the right thing to actually match the prompt, of course.
Use autoinspect(1).
Start it, then run all your commands manually, inspect the script.exp file it created. It should have recorded the exact matched strings, in case there is some weird shell coloring going on which makes you missing your expected string but having no idea why.

EXPECT Script for Cisco / Juniper Config Backup

Firstly I am asking this as a beginner in scripting, currently I have an expect script which automatically logs in to predefined Cisco devices and runs certain commands, I would like to update my script so that this same script can also backup Juniper devices.
Ideally what I would like the script to do is (in pseudo code)
login / send credentials
expect prompt
send command "show version"
if output contains "JUNOS" then
send command 1
send command 2
send command 3
otherwise if output contains "Cisco" then
send command 1
send command 2
send command 3
Im sorry if this has been asked before, but I have tried searching and searching and couldn't find an answer if anyone can assist with this I would really appreciate it. I have also included my current expect script for your reference (this script gets called by a BASH Script)
set timeout 5
set hostname [lindex $argv 0]
set username "user"
set password "pass"
spawn ssh $username#$hostname
expect "Password:"
send "$password\n"
expect "#" {
send "terminal length 0\n"
send "show running-config\n"
expect "end\r"
send "\n"
send "exit\n"
}
---- UPDATE ---
Thanks for your input Dinesh - I have updated my script to include what you provided (as below)
set timeout 5
set hostname [lindex $argv 0]
set username "user"
set password "pass"
set prompt "(>|#|\\\$) $"
spawn ssh $username#$hostname
expect "*assword:"
send $password\r
send \r
expect -re $prompt {
send "show version\r"
expect -re $prompt
expect *;
set output $expect_out(buffer);
#Basic string check logic with Tcl
if { [string first "JUNOS" $output ]!=-1 } {
send "show configuration | display set | no-more"
expect -re $prompt
send "exit\r"
} else {
send "terminal length 0\r"
expect -re $prompt
send "show run\r"
expect "end"
send \r
expect -re $prompt
send "exit\r"
}
}
However when i run this script the issue I have is that the output of the "show version" doesn't seem to be matching my "string check" and the script therefore ignores the "if" statement and proceeds with the "else" statement.
The output of the "show version" command is below - what will I need to modify so that the "JUNOS" string gets matched?
user#host> show version
Hostname: host
Model: srx240h
JUNOS Software Release [11.4R7.5]
--- EDIT 2: Output from the script
05c4rw#testpc:~/script$ ./ssh.sh
spawn ssh user#juniperhost
## LOGIN BANNER - Removed for brevity
user#juniperhost's password:
--- JUNOS 11.4R7.5 built 2013-03-01 11:40:03 UTC
user#juniperhost> show version
Hostname: juniperhost
Model: srx240h
JUNOS Software Release [11.4R7.5]
user#juniperhost> show configuration | display set | no-more
set version 11.4R7.5
## *** OUTPUT REMOVED FOR BREVITY / PRIVACY ***
## *** END OF OUTPUT from previous command
user#juniper> spawn ssh user#ciscohost
password:
## LOGIN BANNER - removed for brevitiy
ciscohost#05c4rw#testpc:~/script$
set timeout 5
set hostname [lindex $argv 0]
set username "user"
set password "pass"
spawn ssh $username#$hostname
expect "Password:"
send "$password\r"
expect "#" {
send "terminal length 0\r"
expect "#"
# This is to clean up the previous expect_out(buffer) content
# So that, we can get the exact output what we need.
expect *;
send "show running-config\r"
expect "end"
#Now, the content of 'expect_out(buffer)' has the whole 'show run' output
set output $expect_out(buffer);
#Basic string check logic with Tcl
if { [string first "JUNOS" $output ]!=-1 } {
# Apply your logic here
# send "command1"
# expect "#"
} else {
# Same as above
# I assume, there are 2 possibilities. So, keeping 'else' part alone.
# Have 'else if', if you have more than 2.
}
}
Notice that each line sent by the script is terminated with \r. This denotes a return character and is exactly what you would press if you entered these lines at the shell, so that is exactly what Expect has to send. It is a common mistake to terminate send commands to a process followed by \n. In this context, \n denotes a linefeed character. You do not interactively end lines with a linefeed. So Expect must not either. So, always use \r.
You can have a look at here if you are interested to know more about the why you need expect *. (which is a separate story)
I can see that there are some commands used only with send in your example. Basically, expect will work with two feasible commands such as send and expect. In this case, if send is used, then it is mandatory to have expect (in most of the cases) afterwards. (while the vice-versa is not required to be mandatory)
This is because without that we will be missing out what is happening in the spawned process as expect will assume that you simply need to send one string value and not expecting anything else from the session.
This might not be what you were looking for but thought I will post it just in case.
You might want to look into something like Rancid instead of having those scripts. Rancid will not only backup your device configs, it will also provide you with a diff on the devices it is managing at pre-defined intervals (for e.g if you set the interval to 15 mins, rancid will login to your devices every 15 mins grab the configs, back them up and do a diff with the previous version and show you the diff)

How do you save and parse a command output in Expect?

I am half-way through writing an Expect script on a Linux server which is supposed to telnet to a router in order to collect some system information. So far my script can successfully make the connection, run a router command, disconnect and terminate.
The command displays a few lines which I need to parse, something I am not sure how to do in Expect. How can I save the output, grep a line, then a column from the line, and finally save the result in a file? If possible, I would like to use Expect entirely rather than a work-around (for example Expect embdded in Bash).
Thanks for your time.
jk04
Two tips for expect development:
autoexpect to lay out a framework for your automation
exp_internal 1 to show verbosely what expect is doing internally. This one is indispensable when you can't figure out why your regular expression isn't capturing what you expect.
basically, $expect_out(buffer) [1]. holds the output from last expect match to the current one. you can find your command output there.
and for the string manipulation, you can simply employ the tcl's built-in [2][3].
"How to access the result of a remote command in Expect" http://wiki.tcl.tk/2958
"regexp" http://wiki.tcl.tk/986
"string match" http://wiki.tcl.tk/4385
I've faced and solved a similar problem for interacting with bash. I believe the approach generalizes to any other interactive environment that provides no-op, fixed-string output.
Basically, I wrap the command with two fixed strings and then search for the pattern that includes these strings at the beginning and end, and save the content in between them. For example:
set var "";
expect $prompt { send "echo PSTART; $command; echo PEND;\r"; }
expect {
-re PSTART\r\n(.*)PEND\r\n$prompt { set var [ string trim $expect_out(1,string) ]; send "\r"; }
-re $prompt { set var "" ; send "\r"; }
timeout { send_user "TIMEOUT\n"; exit }
}
I suspect that this approach would work with a shell's comment characters as well, or a simple status command that returns a known output.
Then you can do whatever you need with the content of 'var'.