I am trying to write an expect script that:
1, Logs in to my raspberry pi via SSH
2, Does something in the raspberry pi
3, sends "exit\r" to get back to my computer
4, Does something on my computer
My current sollution looks like this:
#!/usr/bin/expect
set timeout -1
spawn ssh pi#XXXXXXXX
expect "*: "
send "raspberry\r"
expect "pi#"
# Do something here
send "exit\r"
expect "username#"
send "pwd" # pwd just as a test
expect eof
The error I'm getting is:
send: spawn id exp6 not open
while executing
"send "pwd""
I have tried googling, but can't find anything that solves this question.
I would be really thankful if someone could help me!
Well, you're spawning the ssh program. When you exit from the remote session, ssh ends, and now you don't have a running process to interact with.
Expect has another mechanism to invoke external programs: there's spawn but also exec. This does not provide any interactivity: the exec'ed program just runs and exits (and returns its output).
So, it depends on what "something" you want to do:
spawn bash on your local machine to then use with send & expect
exec something.
In addition to the expect(1) man page, since Expect is a Tcl extension, there are also the Tcl commands to use.
Related
Matlab R2015a
Hello,
In order to programmatically ssh connect I made an expect script as explained in
ssh connect and commands programmatically
#!/usr/bin/expect
set login "any_user"
set addr "some_address"
set pw "any_pwd"
spawn ssh -t $login#$addr
expect "$login#$addr\'s password:"
send "$pw\r"
expect "~" ; # put here string from your server prompt
send "mkdir some_dir\r"
interact
I called this script LoginSSH.sh (of course, with my own data), then I run this command in the Linux console without problems:
./LoginSSH.sh
The idea was to run this command from Matlab with system():
scmd='./LoginSSH.sh'
[status,cmdout]=system(scmd)
But for some reason Matlab has some problems to execute the script via system(). This is the output:
status =
1
cmdout =
OpenSSL version mismatch. Built against 100020af, you have 1000107f
send: spawn id exp4 not open
while executing
"send "$pw\r""
(file "./LoginSSH.sh" line 8)
I can even run the command in Python:
import os
from subprocess import call
call(["./LoginSSH.sh"])
again, without any problems.
What could be the reason Matlab won't like to run the shell script?
Thanks in advance.
jleon
Im embedding an expect script inside of a bash script in order to place some ftp files on a server.
Im trying to have the expect script return an output value of success or failure to the bash process so I can act on it, but I am failing miserably.
The script looks like this:
sftper()
{
local filename="$1"
local user="user"
local password="password"
local host="ftpserver"
local in=1
IFS= read -r -d '' expect_commands <<EOD >echo_log
spawn /usr/bin/sftp $user#$host
expect "*assword:"
send "$password\r"
expect "sftp>"
send "put $filename\r"
expect "sftp>"
send "bye\r"
interact
EOD
expect -c "${expect_commands//
/;}"
in="$?"
return "$in"
}
`
Please, what am I doing wrong, and how can I fix this?
The single most surprising thing in your script is that you call interact, which is an Expect command to allow the user to be connected directly to the spawned program. However, it's unlikely to be useful after you have sent a bye command to sftp; a close; exit sequence would be much more expected (to shut down the controlling virtual terminal and quit the Expect script).
Thus, I'd expect something more like:
IFS= read -r -d '' expect_commands <<EOD >echo_log
spawn /usr/bin/sftp $user#$host
expect "*assword:"
send "$password\r"
expect "sftp>"
send "put $filename\r"
expect "sftp>"
send "bye\r"
close
exit
EOD
More generally, you ought to think about what happens if a problem occurs (e.g., if the password has been expired), and it is probably easier to write a full expect script instead of running the gauntlet of all that shell quoting. I've added a few small explanatory comments…
# Assuming this is in a file called do_sftp_put.exp
# set up the variables; first line is how to get a command line argument
set filename [lindex $argv 0]
set user "user"
set password "password"
set host "ftpserver"
# launch sftp
spawn /usr/bin/sftp $user#$host
# login sequence
expect "*assword:"
send "$password\r"
expect "sftp>"
# send the file
send "put $filename\r"
expect "sftp>"
# Shut down
send "bye\r"
close
exit
Then your shell script wrapper becomes much simpler:
sftper()
{
expect /path/to/do_sftp_put.exp "$1"
return $?
}
Not much fancy quoting is required here; this is how everything is designed to work. Yes, the expect script needs careful protection so that only you can read it as it contains live credentials, but you had that problem before in your shell code so that's nothing new.
[EDIT]: Of course, this still doesn't detect errors. For that, you need a more complex script. One of the most common errors is for the password to be wrong (or to not match the user). To solve that one, we take this bit:
# login sequence
expect "*assword:"
send "$password\r"
expect "sftp>"
and upgrade it to something like:
# login sequence
expect "*assword:"
send "$password\r"
expect {
"password" {
# It's asking for the password *again*; must be wrong!
# Kill the sftp program and exit with an error code
close
exit 1
}
"sftp>"
}
Working out how to detect an error isn't always trivial (you can use regular expressions and so on, and you need to understand exactly what you are working against), and in some cases it's actually better to back up and try another approach entirely. For sftp specifically, I would be trying to get an SSH identity configured so that I could use key-based auth instead of password-based auth, as that's significantly more secure in practice in several ways. However, that's a substantial change to your operations so it is definitely out of the scope of the question.
I'm trying to execute commands remotely over ssh using an expect script that will log in with a password. So far I have the following script (details changed of course):
#!/usr/bin/expect
spawn ssh user1#cpu3.lab.ie
expect "password:"
send "psw123\r"
expect "$"
send "mkdir pswdtest\r"
I name this script testpswd.sh and then runchmod +x testpswd.sh, d2u testpswd.sh and ./testpswd.sh.
Apparently the script manages to login because I get the Last login:... prompt. However, after this the script seems to wait for a bit and then exit back out of ssh, without making a directory called pswdtest (as I can check afterwards).
I have tried looking up tutorials etc and changing the script above in all ways I could think of, e.g. expect "user1#cpu3:~$"instead of expect "$" and so on.
I'm running windows and use cygwin (hence the d2u), and the network I'm logging into uses Linux.
Any ideas?
While developing an expect script, always add exp_internal 1 at the top of the script: expect will show you what it's matching (or not).
Perhaps you could match the prompt with this: expect -re {\$\s*$}
After you send something, you should expect something
exp_internal 1
set prompt {\$\s*$}
spawn ssh user1#cpu3.lab.ie
expect "password:"
send "psw123\r"
expect -re $prompt
send "mkdir pswdtest\r"
expect -re $prompt
send "exit\r"
expect eof
In the below code (which will run on my linux machine), I am trying to copy run_check_node.sh to two different remote linux machine and then executing that script from my machine only.
Issue: The run_check_node.sh script takes around 25 minutes to execute. But the run_check_node.sh script execution on the remote linux machine stops after around 3-4 minutes and then next iteration of for loop is taken. Same thing happen in this next iteration also. I tried using set timeout 1200, to account for those 25 min, but still same issue. Can anyone please help what to do?
Question: The machine IPs I am using, sometimes they ask for password and sometimes don't. So, I am expecting for it. So, in case no password is asked, it simply throws some message on console and below script execution continues. Please tell if this is correct way of handling of whether password will be asked or not?
#!/bin/bash
Linux1_ip=10.20.30.40
Linux2_ip=10.100.20.30
pass="Gaur"
IP=("$Linux1_ip" "$Linux2_ip")
home="/post_checks"
############################ Linux_ip CHECKS ####################################
for ne in "${IP[#]}"
do
expect -c "
spawn ssh root#$ne \"mkdir /post_checks_$ne\"
expect yes/no { send yes\r; exp_continue }
expect password { send $pass\r; exp_continue }
exit
"
expect -c "
spawn scp $home/run_check_node.sh root#$ne:/post_checks_$ne/
expect yes/no { send yes\r; exp_continue }
expect password { send $pass\r; exp_continue }
exit
"
expect -c "
spawn ssh root#$ne \"chmod +x /post_checks_$ne/run_check_node.sh; bash /post_checks_$ne/run_check_node.sh\"
expect yes/no { send yes\r; exp_continue }
expect password { send $pass\r; exp_continue }
exit
"
fi
done
Now, this may not explain why you're seeing what you're seeing, but it may be a useful workaround.
How about you try to execute your run_check_node.sh shell script on the remote in the background? You would then disconnect and let it complete on its own.
So, basically, your 3rd spawn line would be:
spawn ssh root#$ne \"chmod +x /post_checks_$ne/run_check_node.sh; bash /post_checks_$ne/run_check_node.sh &\"
If you needed to check on completion of the shell script, what I'm proposing would not be acceptable... but I don't see your Bash script doing any checking on that, so I'm thinking it may work for you.
Try it out and let me know how it goes.
I just got some help in automating my password input during an SFTP connection by using Expect.
I then tried to issue a put command and things got complicated again. I'm using the following code:
#!/usr/bin/expect
#!/bin/sh
expect -c "
spawn sftp remoteuser#*.*.*.*
expect \"remoteuser#*.*.*.*'s password:\"
send \"passwrd\r\"
interact "
echo "put output/data.xml\r"
echo "quit\r"
My password input and connection to the remote server works just fine but I am struggling to get the put output/data.xml command to display and execute at the SFTP prompt.
My echo "put output/data.xml\r" and echo "quit\r" lines just print as text which is not entirely surprising but I really don't know what else might work.
I understand you need some help, but you should at least try doing some reading. When you say expect -c 'some stuff ...', you are specifying the entire expect script as the parameter to the -c option. The Expect session will not exist afterward.
Also, try to understand what the interact command does instead of blindly applying what someone advises.
Anyway, the solution:
expect <<'END_EXPECT'
set timeout -1
spawn sftp remoteuser#1.2.3.4
expect "[Pp]assword:"
send "passwrd\r"
expect "whatever the sftp prompt looks like"
send "put output/data.xml\r"
expect "whatever the sftp prompt looks like"
send "quit\r"
expect eof
END_EXPECT
Note that the here-doc delimiter is quoted when you first see it. That way, you will not be subjected to any shell expansions. It's like the entire here-doc is single-quoted.