Import expect script into existing expect script - expect

Is it possible to import common expect functions into another expect script?
The use case is I have a server accessed via serial console port, so there is a long list of steps to connect and access the serial console. There are several expect scripts that all need this initial setup and login. Ie a sample script (ignoring the serial connect steps) might be:
#!/usr/bin/expect
set host [lindex $argv 0]
set user [lindex $argv 1]
set pass [lindex $argv 2]
spawn ssh $user#$host
set timeout 5
# Login to host
expect {
"assword:" {
send -- "$pass\r"
}
}
expect {
// DO OTHER EXPECT STUFF
}
Is it possible to put the login into a common file that is imported/included in every script to setup the initial shell connection and login?
login.exp:
# Login to host
expect {
"assword:" {
send -- "$pass\r"
}
}
foo.exp:
#!/usr/bin/expect
set host [lindex $argv 0]
set user [lindex $argv 1]
set pass [lindex $argv 2]
spawn ssh $user#$host
set timeout 5
####include login.exp
expect {
// DO OTHER EXPECT STUFF
}

Related

issues with spawn id <idx> not open when executing script from iTerm2

I am trying to write a script and execute under iTerm2 profile. However whenever I open the new tab i run into this error. but I execute the same script on Terminal and no problem for it. anyone may know why?
expect: spawn id exp6 not open
`while { 1 } {
(file "xxxxx" line xx)
....
#!/usr/bin/expect
set username [lindex $argv 1]
set hostname [lindex $argv 2]
set password "abcd"
set timeout 60
# trap SIGWINCH and pass to spawned process
trap {
set rows [stty rows]
set cols [stty columns]
stty rows $rows columns $cols < $spawn_out(slave,name)
} WINCH
spawn ssh -o NoHostAuthenticationForLocalhost=yes -o UseKeychain=yes $username#$hostname
while { 1 } {
expect "Are you sure you want to continue connecting (yes/no)" {
send "yes\r"
} "Enter passphrase for key " {
interact
break
} "password:" {
send "$password\r"
}
}
To expect several things simultaneously, don't use while use exp_continue
expect {
"Are you sure you want to continue connecting (yes/no)" {
send "yes\r"
exp_continue
}
"Enter passphrase for key " {
interact
}
"password:" {
send "$password\r"
interact
}
}
If you get the connection prompt, send yes and loop back to the expect statement.
If you get prompted for the passphrase, just drop into interactive mode.
If it's the password prompt, send the password prompt and go interactive.
When the user disconnects, interact will return, the expect statement returns, and the script exits.

How to write a conditional expect script to accept different set of passwords based on servers?

My requirement is to write an expect script that will expect different set of passwords for different set of jump servers. Trouble is that I cannot generalize the
expect 'Password:'
as it is common between different servers. So my only way to identify the server is to read the banner before hand and check for a specific string and send the password accordingly.
I do not know how to read the banner in an expect command for spawn ssh
My code is as below
#!/usr/bin/expect
set host [lindex $argv 0]
set pass1 'xxx'
set pass2 'yy'
spawn ssh $host
expect "Password:*"
send "$pass1\r"
expect {
"Password:*" {
send "$pass2\r"
exp_continue
}
}
It seems strange that you would need to identify the server based on some string in a banner it returns when you already have its identity in the $host variable. Unless the specified hostname resolves to multiple servers that do not share a common password.
Assuming that situation and a banner returned by each host saying "Welcome to servername", you can do something like this:
#!/usr/bin/expect
set host [lindex $argv 0]
spawn ssh $host
expect {
-ex {Welcome to server1} {
set pass "xxx"
}
-ex {Welcome to server2} {
set pass "yy"
}
default {
# Something unexpected happened
exit 1
}
}
expect {
-ex "Password: " {
send "$pass\r"
}
}
This first looks for the welcome message and sets the pass variable to the password for the specific server. Then it sends the selected password to the server after it finds the Password prompt.

"spawn id exp5 not open" Error when when scp'ing with an expect script

I have the an expect script that starts like the following:
#!/usr/bin/expect -f
set timeout 1200
set prompt {[#$] }
set server [lindex $argv 0]
set username [lindex $argv 1]
set password [lindex $argv 2]
set package [lindex $argv 3]
spawn scp -o StrictHostKeyChecking=no "$package" ${username}#${server}:~
expect {
timeout { send_user "\nscp connect time out\n"; exit 1 }
"*assword"
}
after 3000
send "$password\r"
# wait for upload complete
expect {
timeout { send_user "\nscp timeout\n"; exit 1 }
eof
}
....
close
It errors out with the following message after ~15 s:
send: spawn id exp5 not open
while executing
"send "$password\r""
What could be the reason for this? What does exp5 mean?
I have also tried removing the "after 3000" statement, but I get the same error.
The connection is closed by the time you get to the point where you send your password. I suspect that you don't need to enter a password for that server (ssh keys are set up?), and by the time 3 seconds has elapsed (after 3000), the file transfer is complete.
You can condense your script somewhat:
#!/usr/bin/expect -f
set timeout 1200
set prompt {[#$] }
lassign $argv server username password package
# if your expect version does not have `lassign`, stick to below
#set server [lindex $argv 0]
#set username [lindex $argv 1]
#set password [lindex $argv 2]
#set package [lindex $argv 3]
spawn scp -o StrictHostKeyChecking=no "$package" ${username}#${server}:~
expect {
timeout {send_user "\nscp time out\n"; exit 1}
"*assword" {send "$password\r"; exp_continue }
eof
}
This loses the distinction between connection timeout and transfer timeout though.

Expect script not executing after ssh login

I'm trying to write an expect script to:
Connect to a remote server providing the user and password
Loop through a local file reading each line
Execute a specific command on the remote server for each of those lines
I could successfully achieve the step #1 and was testing the #3 with a simple scenario, but couldn't make it work yet. Unfortunately in the line 8 of the script, after sending the password, I just get logged into the server as I would have been logged manually (I can interact with the console) and the rest is not executed.
How can I circumvent this problem?
This is the script:
#!/usr/bin/expect
set timeout 20
set ip [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
spawn ssh -t -t "$user\#$ip"
expect "Password:"
send "$password\r";
expect "NYXOOBPN402(config)$"
send "delete decoders:ASX-Trade24-FIX.mdp\r"
expect "Are you sure you want to delete 'decoders:ASX-Trade24-FIX.mdp' (y/n)?"
send "y\r";
And this is how I'm executing it:
./test_expect.sh 172.18.250.20 admin admin
The problem is that my expect was incorrect in the line 17. Instead of "NYXOOBPN402(config)$", I should just put "*(config)*", since there's a lot of text before this part that was not being matched.
This is my final script for someone that runs into this same issue:
#!/usr/bin/expect
set timeout 9
# Check if the parameters are correct
if {[llength $argv] == 0} {
send_user "Usage: ./test_expect.sh ip username password\n"
exit 1
}
# Read the file with all the decoders names to be deleted
set f [open "decoders.txt"]
set decoders [split [read $f] "\n"]
close $f
# debug mode - very useful:
#exp_internal 1
# Connect to the server
set ip [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
spawn ssh -t "$user\#$ip"
expect "Password: "
send "$password\r";
sleep 3
# send ctrl+c since this terminal shows a lot of decoders
#send \x03
expect {
default { send_user "\nCould not find the expected value.\n"; exit 1 }
"*(config)*" {
# Loop through all the decoders
foreach decoder $decoders {
#send_user "Removing $decoder\n"
send "delete decoders:$decoder\r"
expect {
"Are you sure you want to delete*" { send "y\r" }
"*decoder will still be active*" { send_user "\nRemoved $decoder successfully\n" }
"*no such file or directory" { send_user "\nDecoder $decoder already deleted.\n" }
default { send_user "\nNot expected value with $decoder, please debug.\n"; exit 1 }
}
}
}
}

Expect script issue with if statement

I trying different approach to update existing expect script that I have inherit...
What I trying to achieve is to check Patch Level and depending on that, execute a set of commands.
But I can't make it work. :(
I tried as to below example and also tried with 'if' statement but I am clearly to fresh to solve this task.
I would be very grateful for some feedback what I am doing wrong.
#!/usr/bin/expect -f
# Destination: user#host
set destination [lindex $argv 0]
# Destination password
set pass [lindex $argv 1]
# Expect timeout
set timeout 1000
spawn ssh $destination
expect {
"hostname nor servname provided, or not known" {
exp_close
}
"yes/no)?" {
send "yes\n"
exp_continue
}
"assword:" {
send "$pass\n"
exp_continue
}
"#" {
log_file "./tmp/$destination.log"
send "cat /etc/*release | grep PATCHLEVEL\n"
expect {
"PATCHLEVEL = 3" {
send "zypper ar http://xxx.xxx.xxx.xxx/up2date/SLES11-SP3-LTSS-Updates/sle-11-x86_64/ SLES11-SP3-LTSS-Updates && zypper mr -r SLES11-SP3-LTSS-Update;zyppe
r ref -s;zypper --non-interactive update\n"
log_file
send "exit\n"}
"PATCHLEVEL =4 " {
send "zypper --non-interactive update\n"
log_file
send "exit\n"}
exp_close
}
}
}

Resources