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
Related
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.
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 can't find a way to pass CVS the password to run it automatically on the test server. I am trying to make a expect script.
I have a ssh alias, for which I made an ssh-copy-id, so the pasword handeling of ssh is no problems.
I would like to emulate :
ssh name;
run_remote_script.sh
#(which triggers cvs update at the good place. Nb, the server has a MOTD)
expect "Password:"
send "${CVS_REMOTE_PASSWORD}\n" #enter the password
I made that :
#!/bin/bash
SOFT=$(basename $0)
if [ -z ${CVS_REMOTE_PASSWORD+x} ];then
echo "$SOFT : Undefined variable \$CVS_REMOTE_PASSWORD"
fi
hostname="company"
/usr/bin/expect <<EOD
spawn ssh $hostname
cvs_company.sh
expect "Password:"
send "$CVS_REMOTE_PASSWORD\n"
interact
EOD
I would like to point out that typing ssh company, though 'company' is a placeholder, in my console works perfectly fine.
What is wrong with my script ? It yields "Invalid command name cvs_company.sh while executing cvs_company.sh"
Is there a proper way of doing that ?
Sounds like you need something like this:
expect << EOD
spawn ssh $hostname
expect "YOUR-SHELL-PROMPT"
send "cvs_company.sh\r"
expect "Password:"
send "$CVS_REMOTE_PASSWORD\r"
interact
EOD
I'm not sure about how CVS authentication works, but see if you can do this:
ssh $hostname "echo '$CVS_REMOTE_PASSWORD' | cvs_company.sh"
This assumes your password does not contain single quotes. If it does, there are simple-but-ugly workarounds.
Expect is a fantastic tool, but in many cases you don't need to use it.
The expect script returning invalid command while I trying to create a folder after login to server via ssh
This is the below error I got
invalid command name "mkdir"
while executing
"mkdir new"
(file "./connecttotravalour.exp" line 8)
And the code on my expect script is :
#!/bin/bash
#connect to travalour host
spawn ssh travalour#travalour.local
expect "password"
send "P#ssw0rd\r"
interact
mkdir new
Posting an answer form askubuntu for similar question
The main thrust of expect programming is send and expect pairs: you send some text to the spawned process and expect a response. In this case, you send the mkdir command, and expect to see your prompt to know that the command has completed. Prompts are best matched as regular expressions to match the end of it. Since prompts are so configurable, you might want to edit the prompt expression: this one matches a literal dollar sign and a space at the end of the string.
#!/bin/bash
#connect to travalour host
spawn ssh travalour#travalour.local
expect "password"
send "P#ssw0rd\r"
set prompt_re {\$ $}
expect -re $prompt_re
send "mkdir -p new"
expect -re $prompt_re
interact
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.