Expect within a Bash script

@Chris: I incorporated the changes you suggested and my code is working now.

However, I had to make two more changes stated below:

  1. The single quote which you mentioned prevents parameter substitution. For example, I cannot write $IP in place of 1.1.1.1. Hence, to get around this, I removed the single quotes and replaced with double quotes. As you mentioned nested doubles quotes are not interpreted by Bash which is true. Hence I rewrote the inside double quotes as

     send \"password1\r\"
    

    That is adding backslashes before the double quotes inside. This corrects the problem of parameter substitution.

  2. Even after I put two/three actions within a single expect command, as they run in parallel I still faced issues. So taking your suggestion, I put each of the action in a separate Expect command. Something like:

     expect {
         Prompt>          { send "en\r"; exp_continue }
     }
     expect  {
         Password:        { send "password2\r" }
     }
    

The first problem is that the shell does not interpret nested double quotes as you might like. The easiest way to fix this is to put the Expect program in single quotes. This will be sufficient as long as there are no single quotes in the Expect program itself.

The next problem you will run into is that having all the patterns and actions in a single expect command will process them in parallel. What is actually happens is that the first Password: pattern will match any time it sees that string (i.e. even for the admin password the second time around). This will be a problem if the two passwords need to be different. At a minimum, identical patterns will need to go into separate expect commands so that they can be executed sequentially. This problem also affects the Prompt# pattern where you look for it three times and want to send three different responses.

Later, you will get an error after you send the first clear command. Expect interprets square brackets inside double quotes in a way that is similar to how shells interpret $() or `` (i.e. command substitution). You will see an error like this:

invalid command name "confirm"
    while executing
"confirm"
    invoked from within
"expect {  
⋮

It is trying to run confirm as a Tcl (or Expect) command. You can use curly brackets ({}) to prevent Tcl from making this interpretation. Furthermore, expect patterns are treated as “glob” expressions by default (i.e. like shell wildcards), so even if you write {[confirm]} as the pattern, it will still not be used for an exact string match (it would match any single character c, o, n, f, i, r, or m). You must use the -ex flag to mark the pattern for exact matching.

Fix these issues, drop some of the unnecessary quoting, and you might end up with something like this:

#!/bin/sh
VAR=$(expect -c '
  proc abort {} {
    puts "Timeout or EOF\n"
    exit 1
  }
  spawn telnet 1.1.1.1
  expect {
    Password:        { send "password1\r" }
    default          abort
  }
  expect {
    Prompt>          { send "en\r"; exp_continue }
    Password:        { send "password2\r" }
    default          abort
  }
  expect {
    Prompt#          { send "clea line 10\r"; exp_continue }
    -ex {[confirm]}  { send "Y\r" }
    default          abort
  }
  expect {
    Prompt#          { send "clea line 11\r"; exp_continue }
    -ex {[confirm]}  { send "Y\r" }
    default          abort
  }
  expect {
    Prompt#          { send "exit\r"; exp_continue }
    timeout          abort
    eof
  }
  puts "Finished OK\n"
')

echo "$VAR"

The problem is that that the double quotes in the Expect script are treated as the end of the Expect script. Try mixing single quotes with double quotes:

#!/bin/bash
VAR=$(expect -c '
spawn telnet 1.1.1.1
expect {
"Password:" { send "password\r" ; exp_continue}
"Prompt>" { send "en\r" ; exp_continue}
"Password:" { send "password\r" ; exp_continue}
"Prompt#" {send "clea line 10\r" ; exp_continue}
"[confirm]" {send "Y\r" ; exp_continue}
"Prompt#" {send "clea line 11\r" ; exp_continue}
"[confirm]" {send "Y\r" ; exp_continue}
"Prompt#" {send "exit\r" }
}
')

Tags:

Bash

Expect