[Expect Script]Separate log files for each device - expect

My code is generated for one log file output from multiple devices.
How could we separate each device for each log file output?
Here is my code:
#!/usr/bin/expect -f
#Slurp up the input file
set fp [open "ip.txt" r]
# To avoid empty lines, 'nonewline' flag is used
set file_data [read -nonewline $fp]
close $fp
set prompt ">"
log_file -noappend router_status.txt
foreach ip [split $file_data "\n"] {
puts "Router $ip Interface Status"
spawn telnet $ip
expect "Username:"
send "username\r"
expect "assword:"
send "password\r"
expect $prompt
# To avoid sending 'Enter' key on huge configurations
send "show interface description\r"
expect {
-ex "---(more" { send -- " "; exp_continue }
"*>" { send "exit\r" }
}
set timeout 3; # Reverting to default timeout
# Sending 'exit' at global level prompt will close the connection
expect eof
}

You can simply achieve it by changing the log_file
foreach ip [split $file_data "\n"] {
puts "Router $ip Interface Status"
spawn telnet $ip
# Altering 'log_file' for each ip
log_file -noappend router_${ip}_status.log
# Your further code here...
}

Related

Loop in Except Script Variable Getting Value from Text File

I have the following script:
#!/usr/bin/expect -f
# Set variables
set hostname [lindex $argv 0]
set username [lindex $argv 3]
set password [lindex $argv 1]
set devname [lindex $argv 4]
set enablepassword [lindex $argv 2]
set conft 0
# Log results
log_file -a ~/scripts/ExpectScripts/9120CiscoAPs/results.log
# Announce which device we are working on and at what time
send_user "\n"
send_user ">>>>> Working on $hostname $devname # [exec date] <<<<<\n"
send_user "\n"
# Don't check keys
set timeout 30
spawn ssh -o StrictHostKeyChecking=no -o ConnectTimeout=30 $username\#$hostname
expect "User:"
send "$username\n"
expect "Password:"
send "$password\n"
expect "*>"
set f [open "aprebootlist2.txt"]
set aps [split [read $f] "\n"]
close $f
foreach ap $aps {
send "config ap reset $ap\n"
expect {
"*n)" {send "y\n"}
"*invalid." {send "\r"}
}
expect eof
close
}
send "\r"
expect "*>"
send "logout\n"
expect "*(y/N)"
send "n\r"
exit
This should log into a cisco wireless controller and basically run the command "ap config reset apvariable" a bunch of times. I tried to create a loop getting the variable for ap name from a text file. The script logs into the controller, reads the first name in the file, than fails with the below error. What am I doing wrong?
Error is:
Cisco Controller) >send: spawn id exp6 not open
while executing
"send "config ap reset $ap\n""
("foreach" body line 2)
invoked from within
"foreach ap $aps {
send "config ap reset $ap\n"
expect {
"*n)" {send "y\n"}
"*invalid." {send "\r"}
}
expect eof
..."
(file "./rebootapcommands2.exp" line 29)

use expect to check if command output contains a word (else error)

I want to check if there is an interface in the output of the command:
ip a
If there is not, then I want to stop the execution. I've tried this code:
#!/usr/bin/expect -f
spawn bash
set spi_bash $spawn_id
send "ip a\r"
expect {
-re "docker0" {puts "docker0 is there\n"; exp_continue }
timeout {puts "no docker0\n"; exit 1}
}
exit
but exp_continue continues to timeout case.
I'd suggest you don't need expect for this:
set output [exec ip a]
if {[string first docker0 $output] != -1} {
puts "no docker0"
}
But if this is part of a larger expect program, you can write
spawn bash
set spi_bash $spawn_id
# a regex matching your prompt: a dollar sign, a space, and end of input
# adjust as required
set prompt {\$ $}
send "ip a\r"
set has_docker false
expect {
-re "docker0" {set has_docker true; exp_continue}
-re $prompt
}
send "exit\r"
expect eof
if {$has_docker}
puts "docker0 is there"
} else {
puts "no docker0"
}
exit [expr {!$has_docker}]

lines from a file as variable in expect script

Need some help creating a loop in expect script where the variable is extracted from lines in a file.My current expect script is like below;
#!/usr/bin/expect -f
set i [open "samplelist"]
set hosts [split [read $i] "\n"]
set timeout -1
foreach host $hosts {
spawn /usr/bin/ssh appadm#$host
expect "appadm#$host:~>"
send "su epos\r"
expect "Password:"
send "pa55w0rd\r"
expect "epos#$host:/home/appadm>"
send "grep playlist /appl/epos/bin/cron.epos\r"
expect "epos#$host:/home/appadm>"
send "exit\r"
expect "appadm#$host:~>"
send "exit\r"
}
expect eof
close
However when i ran this script it does not terminate correctly after reading the last line of the file
Final modified expect script after being guided by Glenn
#!/usr/bin/expect -f
set i [open "samplelist"]
set hosts [split [read -nonewline $i] "\n"]
set timeout -1
foreach host $hosts {
spawn /usr/bin/ssh appadm#$host
expect "appadm#$host:~>"
send "su epos\r"
expect "Password:"
send "p#ssw0rd\r"
expect "epos#$host:/home/appadm>"
send "grep playlist /appl/epos/bin/cron.epos\r"
expect "epos#$host:/home/appadm>"
send "exit\r"
expect "appadm#$host:~>"
send "exit\r"
expect eof
}
The scripts reads a list of hosts from a file, loop connects and run a command to the remote host until the end of the list.

expect for ssh passwordless

i have a file that has ip addresses, locations of files and passwords. I cannot use ssh key authentification, that's why i'm limited to use expect inside a bash script. the file contain the information as follow, seperated by spaces:
ip location password
ip location password
etc
my script is :
VAR=$(expect -c "
set fid [open "file.conf" w]
set content [read $fid]
close $fid
set records [split $content "\n"]
foreach rec $records {
set fields [split $rec]
lassign $fields\ ip location password
puts "$ip"
puts "$location"
puts "$password"}
spawn ssh $ip tail -f $location > /home/log_$ip 2>/dev/null &
expect {
".*Are.*.*yes.*no.*" { send "yes\n" }
"*?assword:*" { send "$password\r" }
}
")
echo "$VAR"
when i run the script, it gives me this error :
wrong # args: should be "read channelId ?numChars?" or "read ?-nonewline? channelId"
while executing
"read "
invoked from within
"set content [read ]"
You need to enclose the expect body in single quotes, so the expect variables are not expanded by the shell before expect starts to execute.
Also, if you hit the "are...yes...no" prompt, then you need to use exp_continue so you can keep expecting the password prompt.
DON'T OPEN THE CONF FILE WITH "w" -- you will destroy the file contents. You are reading from it, so open it for reading
VAR=$(expect -c '
set fid [open "file.conf" r]
while {[gets $fid line] != -1} {
lassign [split $line] ip location password
puts "$ip"
puts "$location"
puts "$password"
spawn ssh $ip tail -n 20 $location > /home/log_$ip 2>/dev/null &
expect {
-re "Are.*yes.*no" { send "yes\r"; exp_continue }
-gl "*?assword:*" { send "$password\r" }
}
}
')
I'm not sure if this will work when you redirect spawn output: I'm worried that expect will the have nothing to work with. If this doesn't work, remove "> /home/log_$ip 2>/dev/null &" or use
spawn ssh $ip tail -n 20 $location 2>/dev/null | tee /home/log_$ip
For backgrounding, you probably have to do something like this (untested)
expect -c '...' > /home/log_$ip 2>&1 &
expect_pid=$!
# ...
# later
wait $expect_pid
VAR=$(< /home/log_$ip)
do something with "$VAR"

How to capture SFTP transfer if successful from expect

I was lurking on this site for quite awhile now because I am doing an SFTP in a expect/SH script. SSH keygen is not an option for us since we don't have access to the remote server so we're looking into using expect to provide password arg for SFTP.
Here is the script I am working on, and everything is working here except I want to capture or log to an output file if my transfer ("mput") completed successfully. Any advice on what code to put after the "mput" since if I add an expect_out(buffer) after, it is failing.
#!/bin/ksh
DIRROOT=/apps/gen/e2k/sys/bpp
COPYDIR=$DIRROOT/SENT
FILEHASH=TEST.SOME.FILE.*
if [ ! -f $COPYDIR/$FILEHASH ]; then
echo "No File"
fi
# New FTP credential from GIC
FTPSERV=**********
FTPUSER=**********
FTPPWD=**********
FTPDIR=/to-scs
/usr/local/bin/expect -f - <<EOFEXPECT1
#exp_internal 1
set timeout -1
set log [open "/dir/dir1/dir2/MIKETEST.txt" w]
spawn sftp -oPort=10022 $FTPUSER#$FTPSERV
expect "password:"
send "$FTPPWD\r";
expect "sftp> "
send "lcd $COPYDIR \r";
expect "sftp> "
send "cd /recipient \r";
expect "sftp> "
send "mput TEST.SOME.FILE.*\r";
put $log $expect_out(buffer)
close $log
expect "sftp> "
send "bye\r";
expect eof
EOFEXPECT1
if [ $? -eq 0 ]
then
echo "success"
else
echo "fail"
fi
You should be using puts instead of put and i wouldn't rely on $expect_out(buffer) for error checking. Rather use a nested expect statement to trap for common sftp scenarios/errors.
set timeout -1
send "mput TEST.SOME.FILE.*\r";
expect {
#Check for progress, note does not work with all versions of SFTP
#If a match is found restart expect loop
-re "\[0-9]*%" {
set percent $expect_out(0,string)
puts $logf "File transfer at $percent, continuing..."
exp_continue
}
#Check for common errors, by no means all of them
-re "Couldn't|(.*)disconnect|(.*)stalled" {
puts $logf "Unable to transfer file"
exit 1
}
#OK continue
"sftp>" {
puts $logf "File transfer completed"
}
}
Finally, i don't recommend using timeout of -1 (never) as this will lead to a stuck process sooner or later. Rather make us of a timeout value and trap for the possibility of a timeout to occur in the expect block.
set timeout 60
expect {
#Process timed out
timeout {
puts $logf "File transfer timed out"
}
}

Resources