There are a couple of things about Expect
that may be non-
intuitive. This section attempts to address some of these things
with a couple of suggestions.
A common expect problem is how to recognize shell prompts. Since
these are customized differently by differently people and
different shells, portably automating rlogin can be difficult
without knowing the prompt. A reasonable convention is to have
users store a regular expression describing their prompt (in
particular, the end of it) in the environment variable
EXPECT_PROMPT. Code like the following can be used. If
EXPECT_PROMPT doesn't exist, the code still has a good chance of
functioning correctly.
set prompt "(%|#|\\$) $" ;# default prompt
catch {set prompt $env(EXPECT_PROMPT)}
expect -re $prompt
I encourage you to write expect
patterns that include the end of
whatever you expect to see. This avoids the possibility of
answering a question before seeing the entire thing. In
addition, while you may well be able to answer questions before
seeing them entirely, if you answer early, your answer may
appear echoed back in the middle of the question. In other
words, the resulting dialogue will be correct but look scrambled.
Most prompts include a space character at the end. For example,
the prompt from ftp is 'f', 't', 'p', '>' and <blank>. To match
this prompt, you must account for each of these characters. It
is a common mistake not to include the blank. Put the blank in
explicitly.
If you use a pattern of the form X*, the * will match all the
output received from the end of X to the last thing received.
This sounds intuitive but can be somewhat confusing because the
phrase "last thing received" can vary depending upon the speed of
the computer and the processing of I/O both by the kernel and the
device driver.
In particular, humans tend to see program output arriving in huge
chunks (atomically) when in reality most programs produce output
one line at a time. Assuming this is the case, the * in the
pattern of the previous paragraph may only match the end of the
current line even though there seems to be more, because at the
time of the match that was all the output that had been received.
expect
has no way of knowing that further output is coming unless
your pattern specifically accounts for it.
Even depending on line-oriented buffering is unwise. Not only do
programs rarely make promises about the type of buffering they
do, but system indigestion can break output lines up so that
lines break at seemingly random places. Thus, if you can express
the last few characters of a prompt when writing patterns, it is
wise to do so.
If you are waiting for a pattern in the last output of a program
and the program emits something else instead, you will not be
able to detect that with the timeout
keyword. The reason is that
expect
will not timeout - instead it will get an eof
indication.
Use that instead. Even better, use both. That way if that line
is ever moved around, you won't have to edit the line itself.
Newlines are usually converted to carriage return, linefeed
sequences when output by the terminal driver. Thus, if you want
a pattern that explicitly matches the two lines, from, say,
printf("foo\nbar"), you should use the pattern "foo\r\nbar".
A similar translation occurs when reading from the user, via
expect_user
. In this case, when you press return, it will be
translated to a newline. If Expect
then passes that to a program
which sets its terminal to raw mode (like telnet), there is going
to be a problem, as the program expects a true return. (Some
programs are actually forgiving in that they will automatically
translate newlines to returns, but most don't.) Unfortunately,
there is no way to find out that a program put its terminal into
raw mode.
Rather than manually replacing newlines with returns, the
solution is to use the command "stty raw", which will stop the
translation. Note, however, that this means that you will no
longer get the cooked line-editing features.
interact
implicitly sets your terminal to raw mode so this
problem will not arise then.
It is often useful to store passwords (or other private
information) in Expect
scripts. This is not recommended since
anything that is stored on a computer is susceptible to being
accessed by anyone. Thus, interactively prompting for passwords
from a script is a smarter idea than embedding them literally.
Nonetheless, sometimes such embedding is the only possibility.
Unfortunately, the UNIX file system has no direct way of creating
scripts which are executable but unreadable. Systems which
support setgid shell scripts may indirectly simulate this as
follows:
Create the Expect
script (that contains the secret data) as
usual. Make its permissions be 750 (-rwxr-x---) and owned by a
trusted group, i.e., a group which is allowed to read it. If
necessary, create a new group for this purpose. Next, create a
/bin/sh script with permissions 2751 (-rwxr-s--x) owned by the
same group as before.
The result is a script which may be executed (and read) by
anyone. When invoked, it runs the Expect
script.