0

My expect script that allows me to log into servers with my ssh key fails if the directory it tries to create is already there.

When I try to create the directory on the target server the whole script fails with this error:

    authorized_keys                                                                                                             100%  408   478.0KB/s   00:00
send: spawn id exp8 not open
    while executing
"send "$password\r""
    (procedure "check_host" line 5)
    invoked from within
"check_host $line"
    ("while" body line 2)
    invoked from within
"while {[gets $fp line] != -1} {
    check_host $line
}"
    (file "./transfer_keys.sh" line 29)

This is my script:

#!/usr/bin/expect

set timeout 20
set user my_user_name
set password my_pass
set auth_keys /home/my_user_name/.ssh/authorized_keys
set ssh_dir /home/my_user_name/.ssh

proc check_host {hostname} {
    global user password auth_keys ssh_dir
    spawn scp $auth_keys $user@$hostname:
    expect "password"
    send "$password\r"
    spawn ssh $user@$hostname
    expect "password"
    send "$password\r"
    expect "$ "                ;# adjust to suit the prompt accordingly
    send "mkdir $ssh_dir\r"
    send "chmod 700 .ssh\r"
    send "mv authorized_keys $ssh_dir\r"
    send "chmod 600 $ssh_dir/authorized_keys\r"
    send "exit\r"
    expect eof

}


set fp [open host.txt r]
while {[gets $fp line] != -1} {
    check_host $line
}
close $fp

If I delete the ~/.ssh directory on the target server and run it again it runs fine!

How can I create the ~/.ssh directory only on the target server if it doesn't exist?

bluethundr
  • 1,005
  • 17
  • 68
  • 141

2 Answers2

1

The scp will not ask for a password if you have already copied the authorization keys, and so expect "password" will timeout after 20 seconds. When the following send "$password\r" is done, the scp will have finished and the connection closed, hence the error.

Typically, expect is used not only to match a pattern, but then to also specify the command to run if it matches. Simply combine the expect and send in this way:

expect "password" { send "$password\r" }

Then the send will only be done if the prompt is seen. You should also add an expect eof for this scp command, to clean up the connection.

meuh
  • 11,500
  • 2
  • 29
  • 45
1

Per @glennjackman, you may benefit from using ssh-copy-id to copy public keys to another server.


You can use the following shell command list to determine whether or not you need to log in using a password:

ssh -o BatchMode=yes $host : || echo "host $host requires password login"
rtx13
  • 2,580
  • 1
  • 6
  • 22