I am attempting to create a bash script that will ssh into remote network devices, run commands based on the model, and then save the output.
At this time I have my expect file that contains the following:
#!/user/bin/expect
set pw xxxxxxx
set timeout 5
spawn ssh [lindex $argv 0]
expect "TACACS Password:"
send "$pw\r"
interact
I have my .sh file that contains variables which allows me to login to separate "host" files based on Model type. It contains:
shopt -s expand_aliases
fpath="path where scripts are located"
opath="MAC_Results.log"
for i in $( cat $fpath/3560hosts )
do
expect script.exp $i >> "$opath"
done
When I run my .sh, everything operates as expected. My issue lies in I do not know how to call my aliases. I have edited the .bashrc and have sourced it. The .bashrc contains the following:
# .bashrc
# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
# User specific aliases and functions
alias loc3560="term length 0; show mac address-table | ex Gi|CPU|Po; exit"
alias locx="term length 0; show mac address-table | ex Gi[1|2]/1|CPU|Vl99|Po1|dynamic|system; exit"
I have also added the aliases within my .sh aliases. but cant seem to get the right syntax. I have tried the following variations but with no success...
for i in $( cat $fpath/3560hosts )
do
expect script.exp $i $loc3560 >> "$opath"
done
and
for i in $( cat $fpath/3560hosts )
do
expect script.exp $i >> "$opath";
$loc3560
done
Would appreciate any suggestions on where to put these to call to them.
Unfortunately I was not able to figure out how to call my functions or aliases within my main .sh script. With that though I found a way to achieve what I wanted via my expect file . Code listed below and it works like a charm. My subdirectory of 21 files has now been reduced to 3!
set loc3560 "show mac address-table | ex Gi|CPU|Po
exit"
set locx "show mac address-table | ex Gi1/1|Gi2/1|CPU|Vl99|Po1|dynamic|system
exit"
set loc4500 "show mac address-table | ex 1/3|1/5|Port-channel|Switch|1/1|1/2|dynamic|system
exit"
set loc888 "show mac-address-table | i FastEthernet
exit"
set loces2 "show mac address-table | i Fa0
exit"
spawn ssh [lindex $argv 0]
expect "TACACS Password:"
send "$pw\r"
expect "#"
send "$ver\r"
expect {
"C3560-IPSERVICESK9-M" {
send "$loc3560\r"
exp_continue
}
"CAT3K_CAA-UNIVERSALK9" {
send "$locx\r"
exp_continue
}
"C3750E-UNIVERSALK9-M" {
send "$locx\r"
exp_continue
}
"CISCO888-SEC-K9" {
send "$loc888\r"
exp_continue
}
"bootflash:/cat4500e" {
send "$loc4500\r"
exp_continue
}
"SM-ES2-24" {
send "$loces2\r"
exp_continue
}
}
interact
expect "#"
send "exit\r"
end
From here I am then able to call my script and output it to a log file in my directory via:
./script.sh >> Results.log
Related
I am trying to set up a expect script that logs in to a remote server and
fetches the 3 last created logfiles. The output (1 line) looks like below:
root#server1:/cluster/storage/var/log/alarms$
Last 3 created files are: FmAlarmLog_20180515_1.log FmAlarmLog_20180516_2.log FmAlarmLog_20180517_3.log
How can I split this output and create 3 different variables (one for each logfile) from this output?
The name of the logfiles always starts with "FmAlarmLog_"
I need to add later the part handling the fetching of those files.
#!/usr/bin/expect -f
set passwd "xxx"
set cmd1 "ls -ltr | tail -3 | awk '{print \$NF}'"
set dir "/cluster/storage/var/log/alarms"
set timeout 1
set prompt1 "*\$ "
log_user 0
spawn ssh admin#10.30.35.36
expect {
-re ".*Are.*.*yes.*no.*" {
send "yes\n"
exp_continue
}
"*?assword:*" {
send $passwd
send "\n"
}
}
expect $prompt1 { send "cd $dir\r"}
expect $prompt1 { send "$cmd1\r"}
set Last3LogFiles {}
expect \n
expect {
-re {^([^\r\n]*)\r\n} {
lappend Last3LogFiles $expect_out(1,string)
exp_continue
}
-ex $prompt1
}
send_user "Last 3 created files are: $Last3LogFiles\n"
send "exit\n"
exit 0
Try this:
expect $prompt1
send "$cmd1\r"
# express the prompt as a regular expression.
# best practice is to match the prompt at the end of the current input,
# I don't know if you have a space at the end of your prompt
expect -re {(.*)\r\n\$ ?$}
# command output is in $expect_out(1,string)
set Last3LogFiles [regexp -inline -all {FmAlarmLog_\S+} $expect_out(1,string)]
I have a statement inside my expect script like this
send "sed -i -e 's/$oldport/$newport/' backup.txt\r"
expect "$ "
However I wish to first check whether the file backup.txt exist and if it does, then edit it.
How do I acheive this?
Thanks
Since expect is an extension of Tcl, all Tcl commands are available to use:
if {[file exists backup.txt]} {
send "sed -i -e 's/$oldport/$newport/' backup.txt\r"
expect "$ "
}
Here is a quick approach by using ls
set file "backup.txt"
send "ls $file\r"
expect {
"\r\n$file" {puts "\nfile exists"}
"cannot access $file" {puts "\nfile not found"}
}
You can just replace puts "\nfile exists" with your statement.
A Slightly modified #asdone's solution.
Works on remote, unlike #glenn jackman’s that tests for the file in local system, on which the expect script is running.
#!/usr/bin/expect --
# settings
set prompt "\$ "
set filename 'example-file.txt'
set response__file_not_found "No such file or directory*$prompt"
set response__file_exists "$filename*$prompt"
send "ls $filename\r"
expect {
$response__file_not_found {
puts "\n\nFile not found\n\n”
# must exit as the negative response matches both cases
exit
}
$response__file_exists {
puts "\n\nFile exists\n\n"
}
}
I have an expect/Tcl script as part of my bash script that logs into a remote router. Now, for testing purposes I am trying to handle the issue of time-out's. My problem is that the expect/Tcl script is not logging to my log file, and when it does it is logging everything the SSH connection is printing to my prompt which is not what I want.
Here's my expect script:
/usr/bin/expect<<EOF
set timeout 5
set send_human {.1 .3 1 .05 2}
set myStamp [exec date +\[%d\/%m\/%Y\ \%T\]]
set log_file ~/mylogfile.log
spawn ssh -o "StrictHostKeyChecking no" "me\#$1"
expect {
"password: " { send -h "mypassword\r" }
"No route to host" { exit 1 }
timeout { send_log "\$myStamp Timed out to $1\n"]; exit 1 }
}
send -h "reboot in 1\r"
sleep 1
send -h "exit\r"
expect eof
EOF
Please bear in mind that this is part of a function within my bash script that is passed the router name, hence the argument $1.
Any ideas?
You want to use the log_file command, not set a log_file variable
log_file ~/mylogfile.log
Other notes:
Tcl has a very nice builtin command to handle time, don't need to call out to date:
set myStamp [clock format [clock seconds] -format {[%d/%m/%Y %T]}]
the # character is not special in Tcl/expect and does not need to be escaped:
spawn ssh -o "StrictHostKeyChecking no" "me#$1"
As noted, log_file logs a transcript of the session. Just to log specific messages, you can use plain Tcl:
/usr/bin/expect <<EOF
proc log_msg {msg {to_stdout no}} {
set log_line "[timestamp -format {[%Y-%m-%d %T]}] \$msg"
set fh [open ~/mylogfile.log a]
puts \$fh \$log_line
close \$fh
if {\$to_stdout} {puts \$log_line}
}
# ...
expect {
"No route to host" {
log_msg "No route to host" yes
exit 1
}
timeout { log_msg "Timed out to $1"]; exit 1 }
}
# ...
EOF
This opens and closes the log for each message, which adds a bit of overhead. If milliseconds are important, open the log in the global scope, and use the global variable holding the file hendle in the log_msg proc.
I am trying to create a script that will log onto a server, run some commands while providing information back to the user.
I can log onto the server fine using the script, my issue is the output I get back. My script is something like this:
#!/bin/bash
/usr/bin/expect << SSHLOGIN
set timeout 600
spawn ssh user#myServer.com
expect {
"Are you sure you want to continue connecting (yes/no)?" {
send "yes\n";exp_continue
}
Password: {
send "$2\n"
}
}
expect {
"# " {
send "echo \"Current Directory is:\" \n"
}
}
expect "# " {
send "pwd \n"
}
expect {
"# " {
send "exit \n"
}
}
wait
SSHLOGIN
& my output is as follows:
spawn ssh user#myServer.com
Password:
You have new mail.
DISPLAY set to user.myServer:0.0
# echo "Current Directory is:"
Current Directory is:
# pwd
/home/user/
The output I am trying to achieve is something like:
spawn ssh user#myServer.com
Password:
You have new mail.
DISPLAY set to user.myServer:0.0
Current Directory is:
/home/user/
I've tried using log_user 0/1, stty etc.. but I can't seem to get it right with those...
Any help would be appreciated.
The problem is that the std output of the spawned process includes both program output and sent commands, the latter just because of echoing from the remote device.
You could manipulate stdout via the log_user command, turning it off while still expecting & capturing, and printing out the output yourself via the "puts" command. Lastly re-enable, if at all needed. The below works because Expect does not read the echoed command until the expect command.
I can't test now so I'll leave you the regexp to match the pwd output (beware of prompts with current paths), but since the point of your question is not the regexp, I figure the following will do for you:
#!/bin/bash
/usr/bin/expect << SSHLOGIN
set timeout 600
spawn ssh user#myServer.com
expect {
"Are you sure you want to continue connecting (yes/no)?" {
send "yes\n";exp_continue
}
Password: {
send "$2\n"
}
}
expect {
"# " {
send "pwd \n"
log_user 0
expect -re "(include_here_between_the_parenthesis_a_regexp_that_matches_your_pwd_output)" {
puts "Current directory is: $expect_out(1,string)"
}
log_user 1
}
expect {
"# " {
send "exit \n"
}
}
wait
SSHLOGIN
As a last comment... why not change the top line to #!/usr/bin/expect and make this an expect script as opposed to a bash one with a here documnet (or whatever that was called)? It's almost pure expect code after all.
Let me know how that goes, and don't forget upvoting or marking the answer if it indeed helped. :-)
I have a bash+expect script which has to connect via ssh to the remote comp (and i can't use ssh keys, need password identification in here), read the file there, find specific line with the "hostname" (like "hostname aaaa1111") and store this hostname into the variable to be used after while. How can i get the value of the "hostname" parameter? I thought that line content will be in $expect_out(buffer) variable (so i can scan it and analyze), but it's not. My script is:
#!/bin/bash
----bash part----
/usr/bin/expect << ENDOFEXPECT
spawn bash -c "ssh root#$IP"
expect "password:"
send "xxxx\r"
expect ":~#"
send "cat /etc/rc.d/rc.local |grep hostname \r"
expect ":~#"
set line $expect_out(buffer)
puts "line = $line, expect_out(buffer) = $expect_out(buffer)"
...more script...
ENDOFEXPECT
When i try to see line variable, i see only this: line = , expect_out(buffer) = (buffer) What is the right way to get the line from the file into the variable?
Or is it possible to open the file on the remote computer with expect, scan the file and get what i need to the variable?
Here http://en.wikipedia.org/wiki/Expect there is an example:
# Send the prebuilt command, and then wait for another shell prompt.
send "$my_command\r"
expect "%"
# Capture the results of the command into a variable. This can be displayed,
set results $expect_out(buffer)
seems that it doesn't work in this case?
You might just want to try and do it all from expect, as expect can control bash.
The following should do what you've described. Not sure if this is exactly what you are trying to do.
#!/bin/sh
# the next line restarts using tclsh \
exec expect "$0" "$#"
spawn bash
send "ssh root#$IP\r"
expect "password:"
send "xxxx\r"
expect ":~#"
send "cat /etc/rc.d/rc.local |grep hostname \n"
expect ":~#"
set extractedOutput $expect_out(buffer)
set list [split $extractedOutput "\n"]
foreach line $list {
set re {(?x)
.*
(*)
-S.*
}
regexp $re $line total extractedValue
if {[info exists extractedValue] && [string length $extractedValue] > 1} {
set exportValue $extractedValue
break # We've got a match!
}
send "exit\r" # disconnect from the ssh session
if {[info exists exportValue] && [string length $exportValue] > 1}{
send "export VARIABLE $exportValue\r"
} else {
send_user "No exportValue was found - exiting\n"
send "exit\r"
close
exit 1
}
# now you can do more things in bash if you like