Expect with ssh not working every time - bash

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.

Related

Expect: How to handle spawn id exp4 not open error

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.

Linux shell: why "send" command doesn't run

I need some help on below code. I just need to achieve some simpliest task. bakcup and replace some ssl certificate files. but it doesn't seem to work. what is wrong with below code:
import_esx() {
local username=root
local backuptimestamp=$(date +"%m-%d-%y-%I:%M:%S_%p")
/usr/bin/expect << EOF
set timeout 30
spawn ssh -l $username $Ip_Address
expect {
"(yes/no)?" { send "yes\r"; exp_continue }
"*?assword: " { send "$CommonPassword\r"; exp_continue}
}
send_user "Backing up current certificates\r"
send "mv /etc/vmware/ssl/rui.key /etc/vmware/ssl/rui.key.$backuptimestamp\r"
send "mv /etc/vmware/ssl/rui.crt /etc/vmware/ssl/rui.crt.$backuptimestamp\r"
send "mv /etc/vmware/ssl/castore.pem /etc/vmware/ssl/castore.pem.$backuptimestamp\r"
EOF
}
thanks
Jerry
expect {
"(yes/no)?" { send "yes\r"; exp_continue }
"*?assword: " { send "$CommonPassword\r"; exp_continue}
}
you exp_continue on each branch: You have to expect something else so you can stop looping.
Before each send, you should expect something, typically the shell prompt. Also, after the last mv command, you need to send "exit\r" and then expect eof
Assuming your shell prompt ends with "dollar space", you can put it all together:
set prompt {[$] $}
expect {
"(yes/no)?" { send "yes\r"; exp_continue }
"*?assword: " { send "$CommonPassword\r"; exp_continue}
-re $prompt
}
send_user "Backing up current certificates\r"
send "mv /etc/vmware/ssl/rui.key /etc/vmware/ssl/rui.key.$backuptimestamp\r"
expect -re $prompt
send "mv /etc/vmware/ssl/rui.crt /etc/vmware/ssl/rui.crt.$backuptimestamp\r"
expect -re $prompt
send "mv /etc/vmware/ssl/castore.pem /etc/vmware/ssl/castore.pem.$backuptimestamp\r"
expect -re $prompt
send "exit\r"
expect eof

Bash, Expect : Execution of command after SSH is not working

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

how to add timout while scp the file to remote server and if no privilege to sudo via expect script

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.

scp command not working inside expect in bash script

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)?" {.

Resources