|
Expect Hints
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.
This material
is excerpted from the O'Reilly book "Exploring Expect" by Don Libes, and
can also be found in the manpage on many UNIX platforms
|