I need to login to a VM and execute some command there. I have gone through all question related to the same topic but did not find any solution with EXPECT. I am using EXPECT as I need to pass the password while using SSH.
I am getting "command not found error" while executing my script but manually, it is working fine.
#!/usr/bin/expect -f
set user [lindex $argv 0]
set to [lindex $argv 1]
set pass [lindex $argv 2]
set command [lindex $argv 3]
puts "$user, $to , $command and $pass ."
# connect via scp
spawn sudo ssh -t -t -v $user#$to << EOF
ls
EOF
#######################
expect {
-re ".*es.*o.*" {
exp_send "yes\r"
exp_continue
}
-re ".*sword.*" {
exp_send $pass\n
}
}
interact
Error received :
spawn sudo ssh -t -t -v username#server_ip << EOF
Invalid command name "ls"
while executing "ls"
(file "./establishSSHConnection.sh" line 10)
It looks like you are trying to send commands to the remote system in a 'here' doc:
spawn sudo ssh -t -t -v $user#$to << EOF
ls
EOF
Instead, you should use 'exp_send' to send the ls command just before 'interact' i.e., remove the 'here' doc:
spawn sudo ssh -t -t -v $user#$to
And put the ls command here at the end:
expect {
-re ".*es.*o.*" {
exp_send "yes\r"
exp_continue
}
-re ".*sword.*" {
exp_send "$pass\r"
}
}
exp_send "ls\r"
interact
EDIT:
Ah, I misunderstood. If you just want to run the command then you'll need to tell the other end to close the connection:
expect {
-re ".*es.*o.*" {
exp_send "yes\r"
exp_continue
}
-re ".*sword.*" {
exp_send "$pass\r"
}
}
exp_send "ls\r"
exp_send "exit\r"
expect {
eof {puts "Connection closed"}
timeout {puts "Connection timed out"}
}
Expect (being built atop Tcl) does not have here documents.
If you want to execute the command remotely and then end the ssh session, do
set command "ls -lrt" ; # for example
spawn sudo ssh -t -t -v $user#$to $command
# ... log in logic
expect eof
Related
My expect script:
foreach host $hostlist {
puts $host
set timeout 3
spawn ssh -t -q -o StrictHostKeychecking=no "$user\#$host"
expect {
-re "closed by remote host" { exp_continue }
-re "RSA key fingerprint" { send "yes\r"; exp_continue }
-re "(P|p)assword: " { send "$pass\r"; exp_continue }
-re $prompt { send "$cmd\r" }
eof
}
...
is throwing this error and exits right away.
spawn ssh -t -q -o StrictHostKeychecking=no myuserid#example.net
**expect: spawn id exp4 not open**
while executing
"expect -re $prompt"
("foreach" body line 12)
invoked from within
"foreach host $hostlist {
puts $host
set timeout 3
spawn ssh -t -q -o StrictHostKeychecking=no "$user\#$host"
expect {
-re "closed by rem..."
(file "./a" line 36)
whenever the ssh login attempt encounters some problem on a host like:
kex_exchange_identification: Connection closed by remote host
I tried to put various "expect" regexps but couldn't make it continue to the next host but it abruptly exits.
Anyone know of a trick on how to make it not exit but move onto the next host?
If it's not possible using tcl/expect, would it be possible using the expect module with Python or Perl?
Thank you for your help in advance!
Change
-re "closed by remote host" { exp_continue }
to
-re "closed by remote host" { continue }
You want to go to the next foreach iteration, not loop within this expect command to look for the next pattern.
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 want to add timeouts if there is no privilege on /tmp folder while scp the file and if there is no sudo privilege while executing the sudo command in below script.Could you please suggest
spawn scp -o StrictHostKeyChecking=no \
/tmp/rem_script.sh $env(user_name)#$env(first_db_node_ip):/tmp
expect "password:"
send -- "$env(rem_password)\r"
expect eof
spawn ssh -o StrictHostKeyChecking=no $env(user_name)#$env(first_db_node_ip)
expect "password:"
send -- "$env(rem_password)\r"
expect -ex $
send "PS1=UGLY-PROMPT'# '\r"
expect "UGLY-PROMPT# "
send "sudo chmod 777 /tmp/rem_script.sh\r"
expect {
"password:" {
send -- "$env(rem_password)\r"
exp_continue
}
"UGLY-PROMPT# " {}
}
send "sudo sh /tmp/rem_script.sh $env(asrm_name) $env(com_str)\r"
expect {
"password:" {
send -- "$env(rem_password)\r"
exp_continue
}
-timeout 60 "UGLY-PROMPT# " {}
}
send "exit\r"
expect eof
The following code in the accepted answer does NOT work:
expect -timeout 60 {
"password:" {
send -- "$env(rem_password)\r"
exp_continue
}
timeout { send_user "timed out!" }
"UGLY-PROMPT# " {}
}
Instead we have to put the -timeout N in the expect {} block. See following example:
[STEP 101] $ cat foo.exp
proc expect_prompt {} {
global spawn_id
expect -re {bash-[.0-9]+(#|\$)}
}
spawn bash --norc --noprofile
expect -timeout 2 {
not-found {}
timeout {
send_user ">>> you should never see this.\n"
}
}
expect {
-timeout 2 not-found {}
timeout {
send_user ">>> instead, you would see this.\n"
}
}
expect_prompt
send "exit\r"
expect eof
[STEP 102] $ expect foo.exp
spawn bash --norc --noprofile
bash-4.4$ >>> instead, you would see this.
exit
exit
[STEP 103] $
You need a "timeout" pattern if you want to do something on the timeout rather than just stop waiting for the listed patterns. Something like the following:
send "sudo sh /tmp/rem_script.sh $env(asrm_name) $env(com_str)\r"
expect {
-timeout 60
"password:" {
send -- "$env(rem_password)\r"
exp_continue
}
timeout { send_user "timed out!" }
"UGLY-PROMPT# " {}
}
Or whatever you want to do when waiting for the other patterns times out.
You may also want to add patterns specifically looking for the error messages if you don't have the sudo or other priviledges that you expect to have.
while [ $FileLine -le $FileListLines ];
do
# extract each line from FileList
str=$(tail -n+$FileLine ../$FileList | head -n1)
hostpath=$username#$ip:$str
export hostpath ip
expect -c '
spawn bash -c "scp -pr $env(hostpath) $env(ip)"
expect {
"(yes/no)?"{
send "yes\r"
expect "*?assword:*"
send "password\r"
}
"*?assword:*"{
send "password\r"
}
}
'
FileLine=$(( $FileLine + 1 ))
done
The above is a part of a bash script. The scp command in the expect block is not working, that is, files from the remote machine are not getting copied to the local machine.
The same scp command with the path and hostname is working fine when being run from the terminal.
Add expect eof at the end of the expect code otherwise the scp process would be killed right after the password is sent. (Also add a space between the pattern and { in the expect {} block though not sure if that's a problem.)
expect -c '
spawn bash -c "scp -pr $env(hostpath) $env(ip)"
expect {
"(yes/no)?" {
send "yes\r"
expect "*?assword:*"
send "password\r"
}
"*?assword:*" {
send "password\r"
}
}
expect eof
'
UPDATE
Just tried and "(yes/no)?"{ would not work. The space between the pattern and { is required so it should be "(yes/no)?" {.
I created script with expect + ssh + bash executed command to automate some process on remote machine.
Problem is that it is not working everytime.
Sometimes it hangs on password:
Sometimes it logins properly but do not execute remote command, just exit after timeout.
Here bash is script:
#!/bin/bash
USER="user"
PASSWORD="password"
MACHINES="host1 host2 host3 host4"
ex() {
expect -c "
log_file ${MACHINE}.log;#
set timeout 8;
spawn ssh $USER#$MACHINE
expect -re \".*ssword: \" {send \"$PASSWORD\n\"}
expect -re \".*host-.* \" { send \"sudo su -\r\"}
expect -re \".*user: \" { send \"$PASSWORD\r\"}
expect -re \".*# \" { send \"/usr/local/bin/REMOTE.COMMAND.sh\r\"}
expect -re \".*# \" { send \"exit\r\"}
expect -re \".*host-.* \" { send \"exit\r\"}
"
}
machines() {
for MACHINE in $MACHINES ; do
ex &
sleep 0.5
done
}
machines
Here is my working solution - if there is something wrong/not efficient comment please:
#!/bin/bash
USER="user"
PASSWORD="password"
MACHINES="host1 host2 host3 host4"
ex() {
expect -c "
log_file ${MACHINE}.log;#
match_max 100000;
set timeout 30;
spawn ssh $USER#$MACHINE
expect {
-re \".*ssword: \" {send \"$PASSWORD\r\"; exp_continue }
-re \".*user#host-.*\" { send \"sudo su -\r\"; exp_continue }
-re \".*ssword.*:.*\" {send \"$PASSWORD\r\"; exp_continue }
-re \".*host-.*# \" { send \"/usr/local/bin/REMOTE.COMMAND.sh\r\"; }
}
expect {
-re \".*host-.*# \" { send \"exit\r\"; }
}
expect {
-re \".*user#host-.*\" { send \"exit\r\"; }
}
"
}
machines() {
for MACHINE in $MACHINES ; do
ex &
sleep 0.5
done
}
machines
I had some similar problem once.
The problem was due to my SSH usage history, and my ~/.ssh/known_hosts data.
You should try add options to your SSH command, so that it always behave the same wether you have already connected the host or not.
For example :
ssh -o PreferredAuthentications=publickey -o StrictHostKeyChecking=no
You might want to use different kind of authentication with option PubkeyAuthentication.
Try this?:
expect -c "
log_file ${MACHINE}.log;#
set timeout 8;
spawn ssh $USER#$MACHINE
expect {
-re \".*ssword: \" {send \"$PASSWORD\n\";exp_continue}
-re \".*host-.* \" { send \"sudo su -\r\";exp_continue}
-re \".*user: \" { send \"$PASSWORD\r\";exp_continue}
-re \".*# \" { send \"/usr/local/bin/REMOTE.COMMAND.sh\r\";exp_continue}
-re \".*# \" { send \"exit\r\"}
-re \".*host-.* \" { send \"exit\r\"}
}
"
Besides, I have never used expect in a background process.