160

I'm trying to use expect in a Bash script to provide the SSH password. Providing the password works, but I don't end up in the SSH session as I should. It goes back strait to Bash.

My script:

#!/bin/bash

read -s PWD

/usr/bin/expect <<EOD
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr@$myhost.example.com'
expect "password"
send "$PWD\n"
EOD
echo "you're out"

The output of my script:

spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr@$myhost.example.com
usr@$myhost.example.com's password: you're out

I would like to have my SSH session and, only when I exit it, to go back to my Bash script.

The reason why I am using Bash before expect is because I have to use a menu. I can choose which unit/device to connect to.

To those who want to reply that I should use SSH keys, please abstain.

Cadoiz
  • 1,446
  • 21
  • 31
Max
  • 12,794
  • 30
  • 90
  • 142
  • I would try to use kermit in this case. It has a *very* robust scripting language http://www.columbia.edu/kermit/skermit.html#scripts – f3xy Aug 21 '18 at 05:39
  • [This page helped me a lot](https://hostadvice.com/how-to/how-to-automate-tasks-in-ssh/) – Cadoiz Oct 25 '21 at 16:01

9 Answers9

101

Mixing Bash and Expect is not a good way to achieve the desired effect. I'd try to use only Expect:

#!/usr/bin/expect
eval spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr@$myhost.example.com

# Use the correct prompt
set prompt ":|#|\\\$"
interact -o -nobuffer -re $prompt return
send "my_password\r"
interact -o -nobuffer -re $prompt return
send "my_command1\r"
interact -o -nobuffer -re $prompt return
send "my_command2\r"
interact

Sample solution for bash could be:

#!/bin/bash
/usr/bin/expect -c 'expect "\n" { eval spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr@$myhost.example.com; interact }'

This will wait for Enter and then return to (for a moment) the interactive session.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Piotr Król
  • 3,350
  • 2
  • 23
  • 25
  • 1
    it works great, thanks. What if I want to type in command once I'm logged in via SSH, what do I need to do? – Max Jan 25 '11 at 13:28
  • This script should return ineractive shell with logged in user. I dont understand question. If you absolutely want to use the same script in bash look at the edited entry. – Piotr Król Jan 25 '11 at 13:49
  • The same way I send the password when prompted, I would like to send system commands once logged in. – Max Jan 26 '11 at 14:43
  • Samlple code was added to post above. Of course this will work until my_commandX don't change returned prompt, if this happens prompt variable should be changed. – Piotr Król Jan 26 '11 at 15:37
  • @pietrushnic can you explain a bit why use "interact -o -nobuffer -re $prompt return", instead of "expect $prompt"? the latter one looks more commonly used.. – Richard Sep 20 '12 at 11:12
  • @Richard best explanation was provided on http://wiki.tcl.tk/3914, "The major difference is that interact sets up a background matching event loop. expect blocks foreground processing (and ignores user input) during it's matching event loop. interact also has a set of switches that allow programmers to spawn two sessions and connect their ptys, thus letting them drive each other." – Piotr Król Sep 27 '12 at 10:09
  • @Abdull mainly because solution is cleaner and more readable. Using expect from bash does not add anything to solution. Also I meant more code sample provided in question and the way it mix expect and bash, then general rule of not mixing those two scripting languages. – Piotr Król Dec 23 '15 at 13:33
  • take a look at [sexpect](https://github.com/clarkwang/sexpect). you can write expect scripts with bash only. – pynexj Apr 23 '18 at 15:49
  • I tried the above and it worked perfectly when I ran the script from the terminal. However, when I run the script from nautilus it does not work. Any idea what could be going wrong and how to fix it? – Kvothe Nov 18 '20 at 18:42
  • You can [consider `send --` for more broad application](https://stackoverflow.com/a/21280372/4575793) – i_want_more_edits Oct 25 '21 at 16:13
  • *Mixing Bash and Expect is not a good way to achieve the desired effect.* [I'd try to use only *`bash`*](https://f-hauri.ch/vrac/expectScr.sh.txt) – F. Hauri - Give Up GitHub Jun 25 '23 at 07:26
57

The easiest way is to use sshpass. This is available in Ubuntu/Debian repositories and you don't have to deal with integrating expect with Bash.

An example:

sshpass -p<password> ssh <arguments>
sshpass -ptest1324 ssh user@192.168.1.200 ls -l /tmp

The above command can be easily integrated with a Bash script.

Note: Please read the Security Considerations section in man sshpass for a full understanding of the security implications.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
dotnix
  • 848
  • 9
  • 13
  • I don't know if it's a good solution, but it certainly simplifies a lot. Thanks – erikbstack Feb 10 '15 at 18:52
  • 12
    Very dangerous from a security perspective -- command-line arguments can be read by any other process on the system. It's possible to overwrite them, and hopefully `sshpass` does that, but even then there's a period while it's still starting up before it's able to do that when the password is available for any/every process to see. – Charles Duffy May 21 '15 at 22:45
  • 1
    @CharlesDuffy Of course, you are correct. `sshpass` is used in a scenario where you have simple test scripts that execute in a local network environment where security is not the top concern. In fact, there is a section in `man sshpass` where a whole section on Security Considerations is explained. Added this to answer, Thanks. – dotnix May 22 '15 at 17:16
  • @erikb85 Usually, a package does all the dirty stuff for you, but, in all cases, these scripts are built just for that usage, then would be BETTER than add your own stuff. This comment is about don't reinvent the wheel. Deal with hard stuff only if no people has dealed yet with it. sshpass it's a good function. – m3nda Jun 05 '15 at 19:36
  • @erm3nda Your comment applies to use OpenSSL instead of implenting your own SSL library. It might be nasty in the details so let experts deal with it. But `sshpass` is not just a very small tool, it's doing something inherently wrong. You should not do it this way. Use RSA key files instead if you don't want to use passwords. It's not about the nasty details, but about it's inherent problem of writing clear text passwords in a line of bash. Avoid such tools! (Of course I'm also use it because I'm lazy, but you really shouldn't actually) – erikbstack Jul 01 '15 at 11:46
  • @erikb85 I actually use keys the the most ssh connections i made, but sshpass has make the work instead of have to manually enter that data. There are so many scenarios. – m3nda Jul 01 '15 at 15:40
  • 2
    For completeness, I mention that "man sshpass" delivers suitable security warnings to the prospective user, points out that "-p" is the least secure way to use it, and offers the "-e" option for taking the password via environment variable, which at least keeps it off the command line. – Ron Burk Apr 14 '16 at 16:27
35

Add the 'interact' Expect command just before your EOD:

#!/bin/bash

read -s PWD

/usr/bin/expect <<EOD
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr@$myhost.example.com
expect "password"
send -- "$PWD\r"
interact
EOD
echo "you're out"

This should let you interact with the remote machine until you log out. Then you'll be back in Bash.

dr-jan
  • 2,124
  • 2
  • 21
  • 22
  • It goes in and goes back out immediately. "you're out" is printed. – Emmanuel Apr 30 '13 at 14:03
  • 10
    I've replaced "interact" and "EOD" with "expect eof" and it worked for me. This is on a Mac. – Emmanuel Apr 30 '13 at 14:08
  • 3
    None of the answers on this page worked for me but @Emmanuel's suggestion to use `expect eof` solved the issue. – moertel Oct 09 '17 at 12:21
  • Remove the `'` from `usr@$myhost.example.com'` and it should work. And perhaps you need to replace `\n` with `\r`, but YMMV – Tino Jun 12 '18 at 18:32
  • Please consider adding [this fix](https://stackoverflow.com/a/21280372/4575793) as some passwords might not work else – i_want_more_edits Oct 25 '21 at 16:12
  • After I followed @Emmanuel's suggestion to use `expect eof`, I was able to login to the server. However I cannot type any cmd, it's not interactive session and logs out automatically soon – MaThMaX Feb 03 '22 at 12:21
28

After looking for an answer for the question for months, I finally find a really best solution: writing a simple script.

#!/usr/bin/expect

set timeout 20

set cmd [lrange $argv 1 end]
set password [lindex $argv 0]

eval spawn $cmd
expect "assword:"   # matches both 'Password' and 'password'
send -- "$password\r"; # -- for passwords starting with -, see https://stackoverflow.com/a/21280372/4575793
interact

Put it to /usr/bin/exp, then you can use:

  • exp <password> ssh <anything>
  • exp <password> scp <anysrc> <anydst>

Done!

damn_c
  • 2,442
  • 2
  • 15
  • 17
  • Great also is for scp with the two arguments. Of course, ssh also with 2 arguments possible. – Timo Nov 07 '20 at 19:37
12

A simple Expect script:

File Remotelogin.exp

    #!/usr/bin/expect
    set user [lindex $argv 1]
    set ip [lindex $argv 0]
    set password [lindex $argv 2]
    spawn ssh $user@$ip
    expect "password"
    send "$password\r"
    interact

Example:

./Remotelogin.exp <ip> <user name> <password>
Vijay S B
  • 1,251
  • 1
  • 13
  • 24
11

Also make sure to use

send -- "$PWD\r"

instead, as passwords starting with a dash (-) will fail otherwise.

The above won't interpret a string starting with a dash as an option to the send command.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Timmah
  • 2,001
  • 19
  • 18
9

Use the helper tool fd0ssh (from hxtools, source for ubuntu, source for openSUSE, not pmt). It works without having to expect a particular prompt from the ssh program.

It is also "much safer than passing the password on the command line as sshpass does" ( - comment by Charles Duffy).

user562374
  • 3,817
  • 1
  • 22
  • 19
  • This is great! It's a bit hard to make it run (at least in ubuntu server) but works smoothly! The config we made: `echo "yoursshpass" | fd0ssh ssh -c -L$port:$ip:$remote_port user@yourserver.com &` THANKS! – JP Illanes Nov 02 '11 at 23:54
6

Another way that I found useful to use a small Expect script from a Bash script is as follows.

...
Bash script start
Bash commands
...
expect - <<EOF
spawn your-command-here
expect "some-pattern"
send "some-command"
...
...
EOF
...
More Bash commands
...

This works because ...If the string "-" is supplied as a filename, standard input is read instead...

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jimbo
  • 4,352
  • 3
  • 27
  • 44
  • You do not need the `-` here, as `expect` reads from `stdin` by default if you invoke it without argument. However it is useful if you want to run it interactively without command prompt (`expect` vs. `expect -`) or in case you do something like `expect -f "$@"` where the first argument shall be a file, even if it looks like an option (starts with `-`). In that case, if `$1` (the file given) is `-` this reads from `stdin`. – Tino Jun 12 '18 at 18:29
  • Questions: 1) can 'some-pattern' use wildcards? 2) What is 'your-command-here' is something like 'git push', should it be quoted? – Erwann Mar 09 '22 at 21:36
3

sshpass is broken if you try to use it inside a Sublime Text build target, inside a Makefile. Instead of sshpass, you can use passh

With sshpass you would do:

sshpass -p pa$$word ssh user@host

With passh you would do:

passh -p pa$$word ssh user@host

Note: Do not forget to use -o StrictHostKeyChecking=no. Otherwise, the connection will hang on the first time you use it. For example:

passh -p pa$$word ssh -o StrictHostKeyChecking=no user@host

References:

  1. Send command for password doesn't work using Expect script in SSH connection
  2. How can I disable strict host key checking in ssh?
  3. How to disable SSH host key checking
  4. scp without known_hosts check
  5. pam_mount and sshfs with password authentication
Evandro Coan
  • 8,560
  • 11
  • 83
  • 144