8

I am struggling with the classic problem of typing password automatically in ssh, and like everybody else I am stumbling in the dark regarding expect. Finally I cobbled together a script that kinda work:

#!/usr/bin/expect -f

# command line args
set user_at_host [lrange $argv 0 0]
set password [lrange $argv 1 1]

set timeout 1

# ssh command
spawn ssh -S ~/.ssh/tmp.$user_at_host -M -N -f $user_at_host

# deal with ssh prompts
expect {
    "*yes/no*" { send "yes\r" ; exp_continue }
    "*assword:" { send "$password\r" ; exp_continue }
}

This script terminates only thanks to the timeout 1 line, without it it simply hangs, and will terminate only by user interaction (^C).

When the spawn line was a straight forward ssh command, the script terminated right away, however this is not your straight forward ssh. The thing that might be different is the -f option that make it run in the background (but I tried the script without it to no avail).

I read that interact or expect eof might help, but I wasn't able to find the correct incantation that will actually do it.

My question (I think) is How to make an expect script, that spawn a background process, terminate without a timeout?


Edit: I should have expected (no pun intended) the "use passwordless ssh authentication" answer. While this is a sound advice, it is not the appropriate solution in my scenario: Automatic testing a vanilla installed system in a trusted environment, where adding trusted keys to the image is not desirable / possible.

Kara
  • 6,115
  • 16
  • 50
  • 57
Chen Levy
  • 15,438
  • 17
  • 74
  • 92

4 Answers4

7

You probably want:

expect {
    "*yes/no*" { send "yes\r" ; exp_continue }
    "*assword:" { send "${password}\r" }
}
expect $the_prompt
send "exit\r"
expect eof

UPDATE

I missed that you are sending a command via ssh. I think all you need is this:

spawn ssh a@b foo bar baz
expect {
    "*yes/no*" { send "yes\r" ; exp_continue }
    "*assword:" { send "${password}\r" }
    eof
}

You'd hit eof when the foo command completes.

glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • This didn't solved my problem, but gave me an important clue: I need some sort of a prompt when done. See my own answer. Thanks. – Chen Levy Jun 30 '10 at 06:22
2

OK, so I found a permutation that seem to work --

First I need a wrapper script that will give an indication when done:

#!/bin/bash
"$@"
echo "done"

Then the expect script becomes:

#!/usr/bin/expect -f
set user_at_host [lrange $argv 0 0]
set password [lrange $argv 1 1]

# no need for timeout 1
set timeout 60

# use the wrapper
spawn wrapper ssh -S ~/.ssh/tmp.$user_at_host -M -N -f $user_at_host

expect {
   "*yes/no*" { send "yes\r" ; exp_continue }
   "*assword:" { send "$password\r" ; exp_continue }
   # use the wrapper
   "done" { exit }
}

Thanks for Douglas Leeder (voted up) and glenn jackman (voted up) for the helpful advice. I will gladly un-accept this answer, and accept any more elegant answer, perhaps one that do away with the wrapper script.

Thank you all for your attention.

Chen Levy
  • 15,438
  • 17
  • 74
  • 92
1

This loop:

expect {
    "*yes/no*" { send "yes\r" ; exp_continue }
    "*assword:" { send "${password}\r" ; exp_continue }
}

Can't terminate any way except timeout or EOF; the two matching lines will exp_continue, so do round the loop again.

Going into the background means basically forking; the parent dies, and the child continues the connection.

Personally, I'd solve the interactive elements differently:

  1. Host keys: Either connect once manually, or insert the key directly in the ssh known_hosts file.
  2. Password: I'd use private/public key authentication. Either use an agent to store the key, or have a password-less key.
Douglas Leeder
  • 52,368
  • 9
  • 94
  • 137
  • Is there a way to exit right after `*password:`? When I tried to replace the `exp_continue` with anything else, it did exit immediately, but didn't seem to `send` the `${password}`. – Chen Levy Jun 29 '10 at 14:43
  • Maybe a `sleep 1` or `expect eof` or `wait`? – Douglas Leeder Jun 29 '10 at 16:20
  • Sorry for miking it, but I am straggling, and perhaps you can help me decrease the number of permutations I have to test: Should `wait eof` / `sleep 1` be inside or outside of the `expect { ... }` "loop"? – Chen Levy Jun 29 '10 at 16:35
  • I'd remove the password exp_continue statement. Then try putting sleep or one of the others after the expect group. – Douglas Leeder Jun 29 '10 at 20:18
  • Thank you for your kind help. Your advice gave me some important clues for my solution. – Chen Levy Jun 30 '10 at 06:24
0

the way i did it:

set user "****"    
set host "127.0.0.1"    
spawn shh $user@$host    
expect "password: "    
send "**??54"    
expect "$"    
interact

(that answers your how to use interact method)

David Bohunek
  • 3,181
  • 1
  • 21
  • 26