0

I have written following expect script:

/usr/bin/expect<<EOF
set SERVER_HOSTNAME "$env(SERVER_HOSTNAME)"

set USERNAME "$env(USERNAME)"
set PASSWORD "$env(PASSWORD)"

set timeout -1

spawn ssh "$USERNAME@$SERVER_HOSTNAME"
expect  {
        "Host key verification failed." { spawn ssh-keygen -R "$SERVER_HOSTNAME"; expect "known_hosts.old"; send_user "Updated host key details."; exp_continue}
        "continue connecting (yes/no)" { send "yes\r"; expect "Permanently added"; exp_continue}
        "assword:" { send_user "Keygen details are correctly mapped for this server\n"}
        }
EOF

Here I want that if the host key of a server can't be verified while spawing "ssh" process, then the nested spawn process "ssh-keygen -R" should remove old key. Then the "ssh" process should try to connect again so that new key can be added corresponding to that server.

But here, after the execution of:

send_user "Updated host key details."

method, expect process is exiting out from this script.

I know the alternative can be to divide this expect call into two steps, as follows:

 /usr/bin/expect<<EOF
set SERVER_HOSTNAME "$env(SERVER_HOSTNAME)"

set USERNAME "$env(USERNAME)"
set PASSWORD "$env(PASSWORD)"

set timeout -1

spawn ssh "$USERNAME@$SERVER_HOSTNAME"
expect  {
        "Host key verification failed." { spawn ssh-keygen -R "$SERVER_HOSTNAME"; expect "known_hosts.old"; send_user "Updated host key details."; exp_continue}            
        "continue connecting (yes/no)" { send "yes\r"; expect "Permanently added"; exp_continue}
       "assword:" { send_user "Keygen details are correctly mapped for this server\n"}
        }


spawn ssh "$USERNAME@$SERVER_HOSTNAME"
expect  {           
        "continue connecting (yes/no)" { send "yes\r"; expect "Permanently added"; exp_continue}
        "assword:" { send_user "Keygen details are correctly mapped for this server\n"}
        }

EOF    

But do we have a way to execute this expect call in one go. In nutshell, I want to know, can we do the nesting of spawn process?

abhijeet pathak
  • 124
  • 1
  • 10

1 Answers1

1

Whitespace goes a long way toward maintainable code: you don't need to squish so many commands per line.

spawn ssh "$USERNAME@$SERVER_HOSTNAME"
expect {
    "Host key verification failed." {
        spawn ssh-keygen -R "$SERVER_HOSTNAME"
        expect "known_hosts.old"
        send_user "Updated host key details."
        exp_continue
    }
    "continue connecting (yes/no)" {
        send "yes\r"
        expect "Permanently added"
        exp_continue
    }
    "assword:" { 
        send_user "Keygen details are correctly mapped for this server\n"
    }
}

I this case, you don't need to interact with ssh-keygen, so use exec to simply call it

    "Host key verification failed." {
        exec ssh-keygen -R "$SERVER_HOSTNAME"
        puts "Updated host key details."
        exp_continue
    }

If you need to spawn something and interact with it, you need to know that there is an implicit variable, spawn_id, created by spawn, and used by expect and send. You'll need to save the spawn_id of the current process before you spawn any other process. For example:

spawn process1
set ids(1) $spawn_id
expect -i $ids(1) "some pattern"
send -i $ids(1) "some string\r"

spawn process2
set ids(2) $spawn_id
expect -i $ids(2) "some pattern from process2"
send -i $ids(2) "some string to process2\r"
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • How can I use exp_continue after process 2. For e.g. if I want to write a script as follows: spawn ssh "$USERNAME@$SERVER_HOSTNAME" set ids(1) $spawn_id expect { "Host key verification failed." {spawn ssh-keygen -R "$SERVER_HOSTNAME"; expect "known_hosts.old"; send_user "Updated host key details."; exp_continue -i $ids(1)} "continue connecting (yes/no)" { spawn $ScriptsDir/TestProcess; send -i $ids(1) "yes\r"; expect -i $ids(1) "Permanently added"; exp_continue -i $ids(1)} "assword:" { send_user "Keygen details are correctly mapped for this server\n"} } EOF – abhijeet pathak Sep 20 '16 at 13:07
  • In above case I am getting following error message: "usage: exp_continue [-continue_timer] while executing "exp_continue -i (1)" I have initialized ids array as: array set ids { 1 "" } – abhijeet pathak Sep 20 '16 at 13:12
  • This is due to the way you are mixing expect code into a shell script. Do you notice how `$ids(1)` got turned into `(1)`? That's the shell substituting the variables *before* expect starts. Change your heredoc to use single quotes so the shell does not touch the expect variables: ` /usr/bin/expect <<'EOF'` – glenn jackman Sep 20 '16 at 13:23
  • I used exec command also as follows: spawn ssh "$USERNAME@$SERVER_HOSTNAME" set ids(1) $spawn_id expect { "Host key verification failed." {exec ssh-keygen -R "$SERVER_HOSTNAME"; puts "Updated host key details."; exp_continue} "continue connecting (yes/no)" { send "yes\r"; expect "Permanently added"; exp_continue} "assword:" { send_user "Keygen details are correctly mapped for this server\n"} } EOF – abhijeet pathak Sep 20 '16 at 13:24
  • I got following output: /.ssh/known_hosts updated. Original contents retained as /.ssh/known_hosts.old while executing "exec ssh-keygen -R "hostname"" invoked from within "expect { "Host key verification failed." {exec ssh-keygen -R "hostname"; puts "Updated host key details."; exp_continue} "continue c..." Here I wanted that the second statement of expect script should also execute which didn't happen: "continue connecting (yes/no)" { send "yes\r"; expect "Permanently added"; exp_continue} Do we have a way to make it possible? – abhijeet pathak Sep 20 '16 at 13:30
  • Either ssh-keygen returned with a non-zero exit status, or it printed something on stderr. Both of those cases cause `exec` to think that some error occurred with the external command, and you'll get "child process exited abnormally. Investigate the Tcl `catch` command ([here](http://tcl.tk/man/tcl8.6/TclCmd/catch.htm) and [here](http://wiki.tcl.tk/902)) – glenn jackman Sep 20 '16 at 13:42
  • I tried to use single quotes around heredoc, but then I got following error: usr/bin/expect< – abhijeet pathak Sep 20 '16 at 13:58
  • Then I tried to use reverse quote, but here I think that expect script is executed in different shell, and nothing is printed on my screen. – abhijeet pathak Sep 20 '16 at 14:00
  • I have updated my expect script with catch statement as follows: spawn ssh "$USERNAME@$SERVER_HOSTNAME" set ids(1) $spawn_id expect { "Host key verification failed." { if {[catch {exec ssh-keygen -R "$SERVER_HOSTNAME"} excuse]} { puts "Updated host key details with exception " } else { puts "Updated host key details."; }; exp_continue} "continue connecting (yes/no)" { send "yes\r"; expect "Permanently added"; exp_continue} ... } Still, exp_continue is not working and script is just moving out of expect statement. – abhijeet pathak Sep 20 '16 at 14:40
  • No, just put quotes around the first "EOF" like I showed. Ah, I see my formatting was bad. Like this: `/usr/bin/expect <<'EOF' # see the quotes only needed here` – glenn jackman Sep 20 '16 at 14:40
  • After updating EOF as 'EOF', Still I am getting following error: usage: exp_continue [-continue_timer] while executing "exp_continue -i $ids(1)" invoked from within "expect { "Host key verification failed." {spawn ssh-keygen -R "$SERVER_HOSTNAME"; expect "known_hosts.old"; send_user "Updated host key details."; ..." – abhijeet pathak Sep 20 '16 at 14:53
  • check your expect documentation. does `exp_continue` take a `-i` option? – glenn jackman Sep 20 '16 at 15:20
  • Yes that's true, exp_continue only takes [-continue_timer] as option. So how can we call exp_continue for Process1, after running spawn command for Process2 ? – abhijeet pathak Sep 21 '16 at 09:05
  • Try `set spawn_id $ids(1); exp_continue` – glenn jackman Sep 21 '16 at 10:32