Using expect

Hello.

I am trying to debug a script of mine.
I am using the ssh-copy-id from a script run by root.

#!/bin/sh
# $1 is user_name  $2 is computer adress
spawn -noecho  ssh-copy-id $1@$2 ]
set timeout 20
send_user "ssh-copy-id started\n"
while {1} {
    expect {
        ^"(Password :)"$ {
            sleep 1
            expect_user -re "(.*)\n" 
            set the_password $expect_out(1,string)
            send_user "Password is : $the_password\n"
            send "$the_password\n"
            break
            }
        timeout {
           send_user "Timeout expired, aborting..."
            exit 1
            }
        eof {
            send_user "EOF reached."
            }
        * {
            send_user "Bad luck : "
            send_user $expect_out(buffer)
            }
    }
}

In the last test * stand for what ever ssh-copy-id has send out

What I want to obtain is :

Start ssh-copy-id by sending "user name" and "address of the computer where to connect"
While 1 ; do


[*]if asked for password then type the password question so that the operator can type the password from the keyboard
[*]if password good exit with success ( if not return to begin of while )
[*]if asked with question about any things then type the question so that the operator can type an answer ( generally  :  yes/no/y/n )
[*]if error then type the error if possible. In any case exit with failed

done

Can anybody help me.

You have a shebang to invoke a Bourne shell or some other shell linked to /bin/sh but immediately you use the expect command 'spawn'

Is your /bin/sh linked to expect?

Correction :

#!/usr/bin/expect -f
# $1 is user_name  $2 is computer adress
spawn -noecho  ssh-copy-id $1@$2 ]
set timeout 20
send_user "ssh-copy-id started\n"
while {1} {
    expect {
        ^"(Password :)"$ {
            sleep 1
            expect_user -re "(.*)\n" 
            set the_password $expect_out(1,string)
            send_user "Password is : $the_password\n"
            send "$the_password\n"
            break
            }
        timeout {
           send_user "Timeout expired, aborting..."
            exit 1
            }
        eof {
            send_user "EOF reached."
            }
        * {
            send_user "Bad luck : "
            send_user $expect_out(buffer)
            }
    }
}

In the last test * stand for what ever ssh-copy-id has send out

But this does not change my question :

 ssh-copy-id   "user name"   "address of the computer where to connect" 

While 1 ; do 

[*]if asked for password then type the password question so that the operator can type the password from the keyboard fi
[*]if password is good exit with success else  return to begin of while fi
[*]if asked a question about any things then type the question so that the operator can type an answer ( generally  :  yes/no/y/n ) fi
[*]if error then type the error if possible ; In any case exit with failed fi

done

---------- Post updated at 23:37 ---------- Previous update was at 19:04 ----------

Have try this , fail also :

#!/usr/bin/expect
set timeout 10
set host_     [lindex $argv 1]
set user_     [lindex $argv 0]
set password_ ""

proc login {host_ user_ password_ } {expect {
"nkown host"   { puts "***Host($host_) is unkown."; return 1 }
"onnection refused"   { puts "***Connection was refused to host($host_)."; return 2 }
"Escape character is '^]'."   { exp_continue }
 -exact "Password: "   { expect_user -re "(.*)\r" ; set the_password $expect_out(1,string) ;  set password_  $expect_out(1,string) ; exp_send "$the_password\r"; return 0 }
timeout    { puts "***Telnet timed out" ; return  3 }
"(.*)\r"    { puts "Try again" ; return 4 }
}
}


catch { spawn -noecho ssh-copy-id $user_@$host_ }

while {1} {
set login_results [login $host_ $user_ $password_ ]

# If successful connection, exit.
if { $login_results == 0 } {
  puts "\r***Connected"
  # Successful telnet session so exit with zero status
  exit 0
}
}

---------- Post updated 22-11-12 at 18:18 ---------- Previous update was 21-11-12 at 23:37 ----------

I am still trying to make it run.
I am testing this

set timeout 9
set user [lindex $argv 0]
set host [lindex $argv 1]
spawn ssh-copy-id $user@$host
expect {
        Password: {
            stty -echo
            send_user "for $user on $host: "
            expect_user -re "(.*)\n"
            send "$expect_out(1,string)\n"
            stty echo
            exp_continue
        }
   }

Running it

linux:~ # ssh-copy-id.expect.9   user_test   x.y.z.t

I get

linux:~ # ssh-copy-id.expect.9   user_test   x.y.z.t
spawn ssh-copy-id user_test@x.y.z.t
Password: for user_test on x.y.z.t: 
Password: for user_test on x.y.z.t: 
Password: for user_test on x.y.z.t: 
user_test@x.y.z.t's password: bidon
linux:~ # bidon

I try to remove the last line (user_test@x.y.z.t's password: )
using this

set timeout 9
set user [lindex $argv 0]
set host [lindex $argv 1]
spawn ssh-copy-id $user@$host
expect {
        -re ^Password: {
            stty -echo
            send_user "for $user on $host: "
            expect_user -re "(.*)\n"
            send "$expect_out(1,string)\n"
            stty echo
            exp_continue
            }
    }
linux:~ # ssh-copy-id.expect.A   user_test   x.y.z.t
spawn ssh-copy-id user_test@x.y.z.t
Password: for user_test on x.y.z.t: 
Password: 
linux:~ # 

As you can see it does not work.
I get only one attempt.

How to allow only >>Password:<< when it starts at beginning of the text sent by ssh-copy-id

What are you trying to achieve? When ssh asks for password - you want to provide the password, when it ask you to confirm the host key - you want to reply with y/n ... and so on.
Why don't you just run ssh-copy-id directly? Or you just what to learn expect?

1�) I would like to write this little function

function ssh-copy-id   {
#param 1 "user name"   
#param 2 "address of the computer where to connect" 

While 1 ; do 



[*]if asked for password then type the password question so that the operator can type the password from the keyboard fi



[*]if password is good exit with success else return to begin of while fi



[*]if asked a question about any things then type the question so that the operator can type an answer ( generally : yes/no/y/n ) fi



[*]if error then type the error if possible ; In any case exit with failed fi


done}

2�) BTW I would like to understood how expect is working

3�) What I had tested when starting that way
During the login process you get 4 tries
During the first three tries you get this message

password :

For the last one you get

some_user_name@some_host_name's password: 

So I began the while loop that way for the first 3 tries :

while {1} {
    expect {
        ^"(Password :)"$ {
            sleep 1
            expect_user -re "(.*)\n" 

And I hoped treat the forth case (and any other message) with this test :

* {
     send_user "Bad luck : "
     send_user $expect_out(buffer)
     }

Any help is welcome.

Best wishes for everybody.

No idea for my question ?

Help is welcome

Hi, JcDole

Why using expect that as far as I used it, can automate interactive sessions and then try to made an interactive session with it?

I use expect like the following:

#!/usr/bin/expect -f
log_file -a /logs/log_EXPECT
set userpass [lrange $argv 0 0]
set ipaddr [lrange $argv 1 1]   
set username [lrange $argv 2 2]
set timeout 10

spawn ssh $username@$ipaddr
match_max 100000
expect_after {
    eof         { send_log "\nEXP-ERROR---Could not connect to $ipaddr\n" ; exit 1 }
    }
sleep 1
expect {
        timeout     { send_log "\nEXP-ERROR---TIMEOUT Could not connect with  $ipaddr\n" ; exit 1 }
    "(yes/no)?"       {send "yes\n"}
    "*?assword:*"     {send "$userpass\n"}
    }
sleep 1
expect {
        "*?assword:*"     {  send_log "\nEXP-ERROR---User password for  $username incorrect or non existing user  $ipaddr\n" ; exit 1 }
        "?"         {sleep 2; send "\n" }
    }
.
.
now you sould be on shell , type more commands to be executed.

Hope this helps on any way.
Regards