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.
Related
Im trying to compile a script that establishes an ssh connecting with a remote machine and searches multiple log files for error messages, my current code is the following:
#!/bin/bash
expect <<-EOF > /home/file
set timeout 3
spawn ssh -oPort=port ip zgrep "error" /dir/dir/file.gz.\[54321\]
expect "Are you sure you want to continue connecting (yes/no)?" { send "yes\r" }
expect "*password: " { send "password\r" }
expect "*#" { send "exit\r" }
EOF
expect <<-EOF >> /home/file
set timeout 3
spawn ssh -oPort=port ip zgrep "error" /dir/dir/file1.gz.\[54321\]
expect "Are you sure you want to continue connecting (yes/no)?" { send "yes\r" }
expect "*password: " { send "password\r" }
expect "*#" { send "exit\r" }
EOF
expect <<-EOF >> /home/file
set timeout 3 ip
spawn ssh -oPort=port ip grep error /dir/dir/file
expect "Are you sure you want to continue connecting (yes/no)?" { send "yes\r" }
expect "*password: " { send "password\r" }
expect "*#" { send "exit\r" }
EOF
expect <<-EOF >> /home/file
set timeout 3
spawn ssh -oPort=port ip grep error /dir/dir/file1
expect "Are you sure you want to continue connecting (yes/no)?" { send "yes\r" }
expect "*password: " { send "password\r" }
expect "*#" { send "exit\r" }
EOF
Now have you may have noticed i connect to the machine 4 times, which feels so inefficient, i have tried to put all of this log searches into 1 ssh connection, like the following:
expect <<-EOF > /home/file
set timeout 3
spawn ssh -oPort=port ip zgrep "error" /dir/dir/file.gz.\[54321\]
expect "Are you sure you want to continue connecting (yes/no)?" { send "yes\r" }
expect "*password: " { send "password\r" }
expect "*#" { send "grep error /dir/dir/file1\r" }
expect "*#" { send "exit\r" }
EOF
but when i try this i get the error
spawn id exp4 not open
while executing
"expect "*#" { send "grep error /dir/dir/file1\r" }"
What is wrong with this code that make it output this error,
All help appreciated
You can use a single command to do all the work required in one expect session:
expect <<-EOF > /home/file
set timeout 3
spawn ssh -oPort=port ip "zgrep error /dir/dir/file.gz.[54321] ; zgrep error /dir/dir/file1.gz.[54321] ; grep error /dir/dir/file ; grep error /dir/dir/file1"
expect "Are you sure you want to continue connecting (yes/no)?" { send "yes\r" }
expect "*password: " { send "password\r" }
expect "*#" { send "exit\r" }
EOF
You will need to modify your quotation " marks and escape characters \ so that the remote shell does not to interpret them. There is no need to wrap the string error in quotation marks.
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
I have a bash script that calls an expect script with a password that initiates the ssh process:
#!/usr/bin/bash
/usr/bin/expect $PWD/sshScript.exp $SSHPASSWORD
The expect script calls the ssh command, waits for prompt to enter password and sends in the password.
#!/usr/bin/expect
set password [lindex $argv 0];
spawn ssh -o "StrictHostKeyChecking no" username#host
expect "Enter your AD Password:" {
send "$password\r"
}
I can get the remote server to ssh correctly and display the [user#host ~]$ but I want to add to the expect script a way to automatically run a bash script that is stored in the same location as the other two scripts.
I've tried to
a) scp the file to the remote and call it in the server but I can't seem to get expect to send any text pass the password
b) do spawn ssh -o "StrictHostKeyChecking no" username#host < secondscript.shto send in the script to run but it won't wait for the password to be entered before trying to run the script.
You may combine you bash and expect script together and use it to copy the 3rd bash script to the remote server, execute it and return the output.
Here's a simple NON-TESTED example:
#!/bin/bash
[... other bash code here ...]
SCRIPT='/path/to/script/to/run/remotely.sh'
LOGIN='test'
IP='your ip or hostname'
LOCATION='/destination/path/to/script/to/run/remotely.sh'
### start expect part here, you may add '-d' after 'expect' for debug
/usr/bin/expect << EOD
### set a 3 minute timeout
set timeout 180
### copy the script you wish to run on the remote machine
spawn scp -o StrictHostKeyChecking=no -p $SCRIPT $LOGIN#$IP:$LOCATION
expect {
timeout { send_user "\n# TIMED OUT. HOST NOT REACHABLE #\n"; exit 3 }
"*assword: "
}
send "your_password\r"
expect {
"*assword: " { send_user "\n# Incorrect Password. Login Failed. #\n"; exit 4 }
"100%" { send_user "\nFile copied\n" }
}
### ssh to remote server to run script
spawn ssh $LOGIN#$IP
expect {
timeout { send_user "\n# TIMED OUT. SSH DAEMON or PORT 22 CLOSED #\n"; exit 6 }
"*assword: "
}
send "your_password\r"
expect {
timeout { send_user "\n# TIMED OUT. PROMPT NOT RECOGNISED! #\n"; exit 7 }
### expect the prompt symbol
-re {[#>$] }
## execute your script on the remote machine
send "$LOCATION\r"
expect {
"enter what you expect here" { send_user "\nRESULT: Message based on what you set the expect to read $IP\n" }
-re {[#>$] }
}
[... other expect code here ... ]
### exit remote ssh session
send "exit\r"
### end of expect part of script
EOD
### continue bash script here
[... other bash code here ...]
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.