Executing scp command from expect script - use of eof - expect

I'm trying to create an expect script to perform and scp command to copy a given file to a remote machine.
If I run the following script, the scp command fails.
#!/usr/bin/expect -f
set ip_addr [lindex $argv 0]
set in_fname [lindex $argv 1]
set out_fname [lindex $argv 2]
set user [lindex $argv 3]
set password [lindex $argv 4]
set timeout 2
if {[llength $argv] != 5} {
send_user "Usage: ./scp_copy.sh <ip_address> <in_fname> <out_fname> <user> <password>\n"
exit 1
}
spawn scp $in_fname $user#$ip_addr:$out_fname
expect {
(yes/no) {send "yes\r"}
timeout
}
expect {
timeout
{
send_user "FAILED TO PERFORM SCP CMD.\n";
exit 1
}
password:
{
send "$password\r"
}
eof
}
... however, if I remove the 'eof' at the end of the second expect clause and instead create a new expect clause at the end, the scp command works.
expect eof
Please can someone explain this. I'm completely new to expect (and bash) scripts and would be grateful for a simple explanation that helps me understand.
What does 'expect eof' do exactly? Does eof indicate that the spawned process has complete? If so, should I introduce another timeout e.g.
expect {
timeout { exit }
eof
}
What is the difference between the eof being inside the second expect clause and it's own separate expect clause? I was expecting the same effect.
Do I need to call 'close' at the end of an expect script?
Thanks.

Related

Expect script for scp 2 remotes does not copy all the files

I have 2 remote machines lets call them A and B.
I want to transfer files from A to B.
My expect script:
#!/usr/bin/expect
set cmd [lindex $argv 0]
set password1 [lindex $argv 1]
set password2 [lindex $argv 2]
spawn bash -c "$cmd"
expect {
-re ".*es.*o.*" {
exp_send "yes\r"
exp_continue
}
-re ".*sword.*" {
exp_send "$password1\r"
exp_continue
}
-re ".*sword.*" {
exp_send "$password2\r"
}
}
The expect script is used in a shell script:
expect $my_path/my_expect_script.exp "scp -r $remote_A_user#$remote_A_host:$file_path/* $remote_B_user#$remote_B_host:$file_path" $remote_A_password $remote_B_password
Always the script returns an error when entering password for the first time, in the same execution the next attempt works.
yes
qa#172.23.0.2's password:
Permission denied, please try again.
Sometimes some files are not copied to remote B.
Do you know how can I manage to execute a scp expect script on 2 remotes.
Thanks,
First, after you authenticate successfully, there are no more commands in the script, so expect exits and that kills scp. To wait for scp to finish, you have to wait for eof
Next, assuming the file transfer takes more than 10 seconds, you should set the timeout value to Infinity: set timeout -1
Last, the 2nd password will never be sent: the first password prompt will always match first. If those are meant to be two distinct password prompts, you need to make them match some unique text about the different hosts.
Also, indentation aids comprehension
spawn bash -c "$cmd"
set timeout -1
expect {
-re ".*es.*o.*" { # <== this is meaningless to the reader: add a whole word
exp_send "yes\r"
exp_continue
}
-re ".*sword.*" { # <== need something more here
exp_send "$password1\r"
exp_continue
}
-re ".*sword.*" { # <== need something more here
exp_send "$password2\r"
exp_continue
}
eof
}

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)

Expect spawn: No such file or directory error

I am writing an Expect script that remotes to a server "server2" and executes fileexists.sh that will find if file exists at a particular location.
Bash script - fileexists.sh is as follows:
#!/bin/bash
if [ -s $1 ]; then
echo "non empty file exists."
else
echo "File doesnot exist or is empty."
Expect script - expec is as follows:
#!/usr/bin/expect
set username [lindex $argv 0]
set password [lindex $argv 1]
set hostname [lindex $argv 2]
set rfile [lindex $argv 3]
set prompt "$username* ~]$ "
spawn ssh -q -o StrictHostKeyChecking=no $username#$hostname
expect {
timeout {
puts "\n\nConnection timed out"
exit 1
}
"*assword:" {
send "$password\r"
}
}
expect {
"$prompt" {
puts "Logged in successfully.
}
}
spawn ./fileexists.sh $rfile
set lineterminationChar "\r"
expect {
$lineterminationChar { append output $expect_out(buffer);exp_continue}
eof { append output $expect_out(buffer)}
}
puts $output
When i call the expect script with ./expec user pass server2 /apps/bin/file.p I get output as "File doesnot exist or is empty." although the file exists at its location on server2.
i checked using expect:
if {[file exists $rfile]} { puts "file exists" }
And output i get is "file exists".
Seems to be something with spawn that I am unable to figure out.
To check on the remote host, do this (I assume you are logging in to a linux machine, or a machine with GNU stat): untested
send -- "stat -c '%s' '$rfile'\r"
expect {
-re {\r\n(\d+)\r\n} {
puts "$rfile exists with size $expect_out(1,string)"
}
-gl {*No such file or directory*} {
puts "$rfile does not exist"
}
}
send "exit\r"
expect eof

expect adding curly brackets to password containing special characters

I wanted to write up a small expect script and another bash script to save the effort of typing password in ssh connection.
Here goes the scripts:
// ssh.exp, the real workhorse
#!/usr/bin/expect -f
# usage: ./ssh.exp host user pass
set host [lrange $argv 0 0]
set user [lrange $argv 1 1]
set pass [lrange $argv 2 2]
spawn ssh $user#$host
match_max 100000
expect "*?assword:*"
send -- "$pass\r"
send -- "\r"
interact
// the bash script to call it
#!/bin/bash
host='my.host.com'
user='someuser'
pass='Qwerty389$'
./ssh.exp $host $user $pass
However, when the test scripts run, the ssh server always complains that the password is incorrect.
I tried escaping the dollar sign, like pass='Qwerty389\$', but to no avail.
Put the debug statement exp_internal 1 into the expect script, and it shows that the password sent is:
send: sending "{Qwerty389$}\r" to { exp6 } // without escaping $
send: sending "{Qwerty389\$}\r" to { exp6 } // escaping $ in password
Not sure why expect put the curly brackets around the password passed to it. I verified that if there is no dollar sign in the password, there would not be the brackets.
Any help?
The shell code needs to quote variables:
./ssh.exp "$host" "$user" "$pass"
The expect code should not treat lists like plain strings. Extract the arguments with
lassign $argv host user pass
or if your expect is too old to have lassign, do
foreach {host user pass} $argv break
or (less DRY)
set host [lindex $argv 0]
set user [lindex $argv 1]
set pass [lindex $argv 2]

How to get return value from shell script in expect script

In expect script im doing ssh and invoking a bash script which returns a value.How do I get the return value of shell script in expect script? Wait will return status of the Spawn but I want retrun value of the shell script. Thanks in advance.
#!/usr/bin/expect
proc auto { } {
global argv
set timeout 120
set ip XXXX.XXX.XX.XX
set user name
set password pass
set ssh_opts {-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no}
set script /path-to-script/test.sh
spawn ssh {*}$ssh_opts $user#$ip sudo bash $script {*}$argv
expect {
"Password:" { send "$password\r"; exp_continue }
timeout { puts "Test failed." }
eof
}
# ssh command is now finished
exp_close
set info [wait]
# [lindex $info 0] is the PID of the ssh process
# [lindex $info 1] is the spawn id
# [lindex $info 2] is the success/failure indicator
if {[lindex $info 2] == 0} {
puts "exit status = [lindex $info 3]"
}
}
auto {*}$argv

Resources