I have multiple systems with bash and ksh prompts:
bash prompts look like : [user1#wanserver bin]$
ksh prompts look like : $ on some and directory-name> on some systems
Using expect how can i match these ksh prompts. I am able to match the bash prompts, but not the ksh one's. I tried below for ksh, but it doesnt work. How can make this working?
#!/usr/bin/expect
set username [lindex $argv 0]
set password [lindex $argv 1]
set rt [lindex $argv 2]
spawn ssh -q -o StrictHostKeyChecking=no $username#$rt
expect {
timeout {
puts "\n\nConnection timed out"
exit 1
}
"*assword:" {
send "$password\r"
}
}
expect {
"$ " {
send "ls -l\r"
}
}
Assuming there's a space after all the prompts, you can use a regular expression:
set prompt {[$>] $}
#...
expect -re $prompt
send "ls -l\r"
The first dollar sign is a literal dollar sign since it's in a bracket expression. The second one is a regex "end of line" anchor.
Instead of:
expect {
"$ " {
send "ls -l\r"
}
use:
expect {
{$ } {
send "ls -l\r"
}
For details, see rules 4, 6 & 8 at http://tcl-lang.org/man/tcl/TclCmd/Tcl.htm
Related
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
}
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.
I am trying to have an expect script inside a bash to login to a router, execute a command and store output in a text file.
#!/usr/bin/bash
FQDN=$1
LogFile=/tmp/Router_${FQDN}.txt
> $LogFile
expect -d <<EOF > $LogFile
set timeout 20
set FQDN [lindex $argv 0]
set Username "user"
set Password "***$$$"
spawn ssh $Username#$FQDN
expect "*assword:"
send "$Password\r"
expect "#"
send "some command\r"
expect "#"
send "exit\r"
sleep 1
exit
expect eof
EOF
cat $LogFile
I am getting the below error message.
system personnel =\r\r\n= may provide the evidence of such monitoring to law enforcement officials. =\r\r\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==\r\r\npassword: "
send: sending "\n" to { exp6 }
expect: does "" (spawn_id exp6) match glob pattern "#"? no
password:
Enter old password:
Based on the error it appears that script is hitting the {return} key "\r" which is not to be sent at password prompt.
I don't have a return once i ssh. Not sure where i am going wrong.
This is my expect script which is working fine. Its only when i code this inside a bash script its failing.
#!/usr/bin/expect -f
set timeout 20
set FQDN [lindex $argv 0]
set Username "user"
set Password "***$$$"
spawn ssh -o "StrictHostKeyChecking no" $Username#$FQDN
expect "*assword: "
send "$Password\r"
expect "#"
send "some command\r"
expect "#"
send "exit\r"
sleep 1
exit
-Abhi
In a here-doc, variables like $Username and $Password are being expanded by the shell, so they're not seen as literals for Expect to expand. Since those shell variables aren't set anywhere, they're being expanded to null strings. As a result, it's executing ssh #$FQDN and sending an empty password.
You need to escape the $ so that Expect can process them.
You also don't need the set FQDN line in the Expect script, since you're using the shell variable for that.
#!/usr/bin/bash
FQDN=$1
LogFile=/tmp/Router_${FQDN}.txt
> $LogFile
expect -d <<EOF > $LogFile
set timeout 20
set Username "user"
set Password "***$$$"
spawn ssh \$Username#$FQDN
expect "*assword:"
send "\$Password\r"
expect "#"
send "some command\r"
expect "#"
send "exit\r"
sleep 1
exit
expect eof
EOF
cat $LogFile
Or you could set them as shell variables, just like FQDN.
#!/usr/bin/bash
FQDN=$1
Username=user
Password="***$$$"
LogFile=/tmp/Router_${FQDN}.txt
> $LogFile
expect -d <<EOF > $LogFile
set timeout 20
spawn ssh $Username#$FQDN
expect "*assword:"
send "$Password\r"
expect "#"
send "some command\r"
expect "#"
send "exit\r"
sleep 1
exit
expect eof
EOF
cat $LogFile
I am trying to ssh into few systems (read from test.txt file) using expect within a shell script and execute commands on each. The script returns an error "invalid command name". Am I using set and expect in an incorrect way here?
#!/usr/bin/expect -f
set username "root"
set pass "mypassword"
set fd [open /home/test.txt r]
set host [read $fd]
foreach line $host {
ssh -o StrictHostKeyChecking=no -n root#$host 'ls; pwd'
expect "User:" { send "${username}\r" }
expect "root's Password:" { send "${pass}\r" }
expect eof
}
Error returned
./expect.sh
spawn ssh -o StrictHostKeyChecking=no -n root#10.1.1.1
10.1.1.2
'ls
invalid command name "pwd'"
while executing
"pwd' "
("foreach" body line 3)
invoked from within
"foreach line $host {
As the error message suggests, expect parsed ; as a command separator, and couldn't handle pwd'.
That's because there are no single-quoted strings in the language.
Expect is tcl, you have to use double quotes: "ls; pwd"
I am using expect script inside bash script. When I use foreach inside expect script, it throws
wrong # args: should be "foreach varList list ?varList list ...? command"
The code:
#!/bin/bash
read -p "Enter username: " username
read -s -p "Enter password: " password
#Expect script
/bin/expect -<<EOD
set SERVERS {100 101 102}
foreach SERVER $SERVERS {
set timeout -1
spawn scp ${username}#plsa${SERVER}.corp.com:/log.2011-11-24 ${SERVER}
expect "*password:"; send "$password\r"
expect eof }
EOD
echo "completed"
Thanks
The heredoc (<<ENDTOK) is subject to shell expansion on the $variables. That means for each of the $variables you want expect to interpret, you'll need to escape the $.
The way to escape something is to prepend a slash to it ($ -> \$).
It appears the username and password are supposed to come from the shell, the rest from within expect, so here's how that should go:
#!/bin/bash
read -p "Enter username: " username
read -s -p "Enter password: " password
#Expect script
/bin/expect -<<EOD
set SERVERS {100 101 102}
foreach SERVER \$SERVERS {
set timeout -1
spawn scp ${username}#plsa\${SERVER}.corp.com:/log.2011-11-24 \${SERVER}
expect "*password:"; send "$password\r"
expect eof }
EOD
echo "completed"
Note the \ in front of $SERVERS and ${SERVER}.
You need to escape dollar signs with a backslash since $name is expended to the value of variable name:
/bin/expect -<<EOD
set SERVERS {100 101 102}
foreach SERVER \$SERVERS {
set timeout -1
spawn scp ${username}#plsa\${SERVER}.corp.com:/log.2011-11-24 \${SERVER}
expect "*password:"; send "$password\r"
expect eof }
EOD
If you quote your here-document delimiter, the embedded script is effectively quoted too:
/bin/expect -<<'EOD'
... expect script as posted ...
EOD