Background
The Expect program is mainly used for simulation of human-computer dialogue, that is, the kind of system asks questions and the person answers yes/no, or the account logs in and enters the username and password, etc. Because this is particularly frequent and cumbersome, many languages have various implementations of their own. Because of this kind of situation is very many and cumbersome, so many languages have a variety of their own implementation. Initially the first Expect was realized by TCL language, so later Expect are roughly reference to the original use and process, overall the general process include:
running program
Procedures require human judgment and input
Expect Match by keyword
Send matching strings to the program based on keywords
The TCL language implementation of Expect is very powerful, I once used it to implement a complete test platform for firewall devices. Because of its ease of use and wide range, almost all scripting languages have implemented a variety of functions similar to Expect, although they are called different, but the principle is not very different!
pexpect is an Expect-like implementation of the Python language. From my point of view, there are still some gaps between its functionality and the TCL implementation, such as the lack of buffer_full events, the lack of expect before/after events, etc., but it's still adequate for general applications.
Basic Usage Flow
The use of pexpect is centered around 3 key commands:
First use spawn to execute a program
Then use expect to wait for the specified keyword, which is printed to standard output by the program being executed.
Finally, when the keyword is found, the send method is used to send a string to the program based on the keyword.
The first step only needs to be done once, but the program will keep looping through the second and third steps to complete the whole job one step at a time. After mastering this concept, using pexpect will be very easy. Of course, pexpect doesn't only have these 3 methods, there are actually a lot of other peripheral methods, so let's explain them one by one.
API
spawn() - executes the program
The spawn() method is used to execute a program, and it returns an operation handle for this program, which can later be manipulated to operate on this program, for example:
process = ('ftp sw-tftp')
The string in spawn() above is the program to be executed, in this case we are opening an ftp connection to the sw-tftp server. The first element of spawn() is the command to be executed, in addition to some other parameters can be specified, for example: ('ftp sw-tftp', timeout=60) specifies the timeout period, these details will be explained later.
process is the program handle for spawn(). All subsequent operations on the program are based on this handle, so it's arguably the most important part. Try to make it as short as possible, or you'll have to type a lot more in later programs. - - - - - - - - - - - - - - - - - - - - - - -
Note: spawn(), or pexpect, does not translate any special characters. For example, the | * characters have special meanings in the Linux shell, but pexpect does not translate them, so if you want to use the correct meanings of these symbols on a linux system you have to add a shell to the runtime, which is an easy mistake to make.
The right way:
process = ('/bin/bash –c "ls –l | grep LOG > log_list.txt"') ()
spawn() There is also a way to call spawn() where the first parameter is the main program and the next parameter is the main program's parameter... Troublesome to understand? Take a look at the actual code:
cmd = "ls –l | grep LOG > log_list.txt" process = ("/bin/bash", ["-c", cmd]) ()
Isn't it clearer that these codes are the same as the one example above?
The options for spawn include the following:
timeout - timeout time
Default value: 30 (in seconds)
Specifies the default timeout for the program. After the program is started there will be output, we also check in the script if the keywords in the output are known and processed, if the program is not found within the specified time it will return with an error.
maxread - cache setting
Default value: 2000 (unit: character)
Specifies how much data to try to read from the command output at once. If you set a larger number, you will read data from the TTY less often.
Set to 1 to disable read caching.
Setting a larger value will improve performance when reading large amounts of data, but will waste more memory. Setting this value in conjunction with searchwindowsize will provide more functionality.
The size of the cache does not affect what is fetched, that is, if a command outputs more than 2000 characters, the previously cached characters will not be lost, but will be placed elsewhere, and you can still fetch the full output when you use (self for the spawn instance here).
searchwindowsize - pattern matching threshold
Default: None
The searchwindowsize parameter is used in cooperation with the maxread parameter, which is more subtle but can significantly reduce the matching time when there are many characters in the cache.
By default, expect() matches the specified keywords all the time: each time a character is fetched from the cache it matches the regular expression once for everything in the entire cache, and you can imagine how low performance can be if the program returns an unusually large number of characters.
Set the value of searchwindowsize to indicate how many characters received at one time before matching an expression, for example, now there is a command will appear a large number of output, but match the keyword is the standard FTP prompt ftp>, obviously to match only 5 characters (including spaces), but by default whenever expect to get a new character from the beginning of the But by default, every time expect gets a new character, it will match these characters from the beginning. If there are already 1W characters in the cache, matching them one at a time is very resource-consuming, so you can set searchwindowsize=10, so expect will only match the keyword from the newest (last obtained) 10 characters, which will significantly improve performance if set to the right value. Don't worry about whether the characters in the cache will be discarded, no matter how much output, as long as there is no timeout, you will always get all the characters, the setting of this parameter only affects the matching behavior.
This parameter is normally set in the expect() command. The pexpect version seems to have a bug where setting it in spawn does not work.
logfile - run output control
Default: None
When a file handle is specified for the logfile parameter, all content from standard input and standard output is written to this file (note that this writing is copy), and if a file handle is specified, the file is flushed each time a command is sent to the program ().
Here's an important trick: if you want to see the output of the spawn process, then you can write that output to something like:
process = ("ftp sw-tftp", logfile=)
In this way you can see the inputs and outputs during the entire execution of the program, which is good for debugging.
One more example:
process = ("ftp sw-tftp") logFileId = open("", 'w') = logFileId
Note: The file contains both the output of the program as it runs and what spawn sends to the program. Sometimes you may not want this because something appears twice, so there are 2 important logfile correlation parameters:
logfile_read - get the contents of the standard output
Default: None
Record everything that is returned from the execution of the program, i.e., remove the commands you issued and include only the portion that is the result of the command:
process.logfile_read =
The above statement will print all the output during the execution of the program on the screen, but generally does not contain the commands you send to the program, but most programs have a mechanism to display back, such as sending commands when the device not only receives the command string, but also the reverse in your terminal to display the string so that you understand which characters have been entered, this time will also be read by this method. The only cases where logfile_read won't get it are those where it doesn't display back, such as when entering a password.
logfile_send - get what was sent
Default: None
Record all content sent to the executing program
process.logfile_send =
The above statement simply prints on the screen what was sent to the program.
cwd - specifies the directory in which the command is executed
Default: None or . /
cwd is used to specify the path under which the command is sent. It is generally used in the send() family of commands. For example, if you want to execute the ls -l command under the /etc directory in Linux, you don't need to use sendline("cd /etc && ls -l") at all. sendline("ls -l", cwd="/etc") is sufficient.
env - specify environment variables
Default: None
Specify the value of the environment variable, this value is a dictionary, if you are sending a command that is going to use some environment variable then you can supply it here
ignore_sighup - whether to filter the SIGHUP signal or not
Default: True
This parameter was introduced in pexpect 3.1. Before 3.1 (e.g. pexpect 2.3), spawn's subroutines filtered the SIGHUP signal, i.e., it was not possible to terminate a subroutine with Ctrl+C. The default value in 3.1 inherits this behavior, but it can be changed by setting ignore_sighup = False. This behavior is inherited by default in 3.1, but can be changed by setting ignore_sighup = False.
delaybeforesend - character send delay
Default value: 0.05
This is a hidden parameter to set the delay before sending the string. The biggest reason for adding this parameter is because a lot of people run into this problem:
When logging in to an FTP program, if you enter your password using a script, it will be displayed directly. This is based on an incredible fact: when logging in to FTP, the server actually prints a prompt asking you to enter your password, then sends a signal to disable the showback function. When a person uses the keyboard to enter the password, it's impossible for them to see the password in the showback because of the high latency of the action, but the script sends the password prompt as soon as it finds it, and the password appears just before the showback is turned off. and the password will appear before the display is turned off. Pexpect solves this problem by waiting 50 milliseconds before sending a character, but you can disable this behavior by setting it to 0 if you don't think it's necessary.
expect() - keyword matching
Once spawn() has started a program and returned the program control handle, you can use the expect() method to wait for the specified keyword. It returns 0 to indicate that the desired keyword was matched, or a number indicating the first keyword in the list, counting from 0, if the following matches are a list.
expect() utilizes a regular expression to match the desired keyword. (Regular expressions are so widely used that almost all languages have support for them; if you don't know how to use them, you can refer to my other document, Regular Expression Reference.)
The way it's used:
# pattern_list A list of regular expressions that are meant to match these # timeout If timeout is not set or is set to -1, the default timeout value is 30 seconds. You can also set it yourself. The # searchwindowsize function is the same as on spawn, but! Note the but! This is actually explained below (pattern_list, timeout=-1, searchwindowsize=None)
The searchwindowsize here is what actually takes effect in the expect() method, and by default it is None, which means that it does a full match for every character it gets from the subprocess, and if the subprocess has a lot of output ...... performance will be very low. If you set it to something else, it means to do a match only once for as many characters as you read from the child process, which will significantly reduce the number of matches and increase the performance.
After testing, for a sub-process with 48,100,000 characters, it takes 73.2730 seconds to complete the process when searchwindowsize is set to 2000; the same sub-process with this parameter set to None takes 1949.6259 seconds, Oh, my Lady GAGA... Oh, my Lady Gaga... that's an exponential increase.
The easiest way to match
('[Nn]ame')
The code above says: match the name keyword in the process handle (representing the ftp connection we started in the spawn method example), where n is case insensitive.
Once the above keyword matches, it will return 0 to indicate a successful match, but what if it never matches? By default, it will wait, but if you set a timeout, it will time out.
Match a range of outputs
In fact, expect() can match a set of outputs, and by examining the matched outputs, we can do different things. For example, in the case of the ftp connection we spawned earlier, if there were different scenarios after we entered the username, we could monitor those different scenarios to do different things, such as:
index = ([ 'Permission Denied', 'Terminal type', 'ftp>', ]) if index == 0: print "Permission denied at host, can't login." (0) elif index == 1: print "Login ok, set up terminal type…" ('vty100') ("ftp>") elif index == 2: print "Login Ok, please send your command" ()
In the code above, the expect method is a list, where each element of the list is a regular expression for a keyword, i.e., we're expecting one of the 3 cases, and expect returns an order value representing which element I've matched (i.e., which case has occurred), which is counted from zero.
After the EXPECT, the following if statement begins to deal with the 3 cases:
Insufficient privileges, this may be a problem with the ftp server, or do not have the account, or anything else, anyway, as long as we find this situation, we will give the user a prompt, and then kill the process
The login is successful, but we need the user to specify the terminal mode before we can actually use it, so we specify vty100 in the code and see if we can actually use it!
The login is still successful, and you can enter commands to operate the ftp server directly, so we prompt the user, and then give the user operational privileges
Also there is a special case, what if 2 are matched at the same time? The short answer is this:
The first content in the original stream that is matched by the keyword is used
The leftmost in the list of matching keywords will be used
Give an example:
# If the content of the stream is "hello world" # index = (["hi", "hello", "hello world"])
The value returned is 1, which means that 'hello' was matched, even though the best match would have been "hello world", but it's still invalid because it came after.
Tips for use
If you want to check or match both and, then you must put them in the match list so that you can process them by checking the returned numbers. If you don't put them in the list, an EOF or TIMEOUT error occurs and the program stops in the middle.
There are some special syntax in the match rules, for example, the first 2 matches in the following rule are case-insensitive, the key is the (?i) match rule, which is equivalent to the or keyword, because after all, it is not a real regular expression engine, so pexpect uses this special syntax:
(['(?i)etc', '(?i)readme', , ])
expect_exact() - exact match
It is used in the same way as expect(), the only difference being that it no longer uses regular expressions in its match list.
Performance-wise expect_exact() is better, because even if you don't use regular expressions but simply use a few characters expect() will convert them to regular expression patterns before searching for them, but expect_exact() won't, and it won't convert out some special symbols.
expect_list() - pre-converted match
is used in the same way as expect(), except that the list of regular expressions it accepts is converted only once.
expect() is slightly clunky in that it converts the internal regular expression every time it's called (there are other ways to avoid this, of course), and if you're calling expect() later in a loop, the extra conversions reduce performance, in which case it's recommended to use expect_list() instead.
Usage:
# The value to use if timeout is -1. # If searchwindowsize is -1, the system default value is also used. process.expect_list(pattern_list, timeout=-1, searchwindowsize=-1)
expect_loop()
Used to get the content from the standard input, the word loop means it will enter a loop, must get the keyword from the standard input to continue down the execution.
Usage:
expect_loop(self, searcher, timeout=-1, searchwindowsize=-1)
send() - send keyword
send() is used as one of the 3 key operations to send the specified string to the program, and there is nothing special about its use, for example:
("ftp>") ("by\n")
This method returns the number of characters sent.
sendline() - sends a string with a carriage return character
The only difference between sendline() and send() is the carriage return newline character after the string being sent, which makes them used in different ways:
Use send() if you only need to send characters.
If you want a carriage return after sending a character, you use sendline().
It also returns the number of characters sent
sendcontrol() - send control signal
sendcontrol() sends control characters to the subroutine, such as <kbd>ctrl+C</kbd> or <kbd>ctrl+D</kbd> or something like that, for example, if you want to send <kbd>ctrl+G</kbd>, then write it like this. Then write it like this:
('g')
sendeof() - sends the EOF signal
Sends the End Of File signal to the subroutine.
sendintr() - sends an abort signal
Sending a SIGINT signal to a subroutine is the Linux equivalent of kill 2, which terminates the subroutine directly.
interact() - gives control to the user
interact() means to give control to the user (or standard input). Normally pexpect takes over all input and output, but there are times when you want the user to intervene, or just to do part of the job, and interact() is useful.
For example:
When logging into an ftp server, you want the user to manually enter the password during the user password entry phase, and then the script does the rest of the work (it is not safe to write the user password in the script).
Just want to complete the login work, for example, to ssh to connect to a distant server, but in the middle to go through several hops, with manual input is really too much trouble, so use the script to jump to the destination server first, and then give control rights back to the user to do the operation.
Usage:
# escape_character means that when the user outputs the character specified here, he or she is done and hands control back to pexpect. (escape_character='\x1d', input_filter=None, output_filter= None)
In detail, this approach gives control to the user (or the user-operated keyboard) and then simply binds standard output, standard error output, and standard input to the system.
The return code can be defined by setting the value of escape_character to <kbd>ctrl+]</kbd> or <kbd>^]</kbd> When the return code is entered, the script takes control back from the user and continues down the line.
close() - stops the application
If you want to close the subroutine midway, you can do so with close, which returns the return value of the program when called.
If force=True is set, the program will be forced to shut down, presumably by sending the SIGHUP and SIGINT signals first, and then the SIGKILL signal if neither of them is effective, and the program will be shut down no matter what.
Calling this method multiple times is allowed, but there is no guarantee that the correct return value will be returned each time. Try not to do this, and just set the value of force if you want to make sure the program is closed.
Here are examples:
(force=True)
terminate() - stops the application
It can be seen as an alias for close() above, since both the functionality and usage are the same.
Kill() - sends the SIGKILL signal
Sends a SIGKILL signal to the subroutine.
flush()
Nothing is done, just for compatibility with the file method.
isalive() - check subroutine operation status
Checks to see if the called subroutine is running, this method is run under non-blocking mode.
A True return indicates that the subroutine is running; a False return indicates that the program is terminated.
isatty() - checks to see if it is running on a TTY device
Returns True to indicate that a device of type tty is open and connected, or False to indicate that it is not connected.
next() - returns the next line
Like manipulating files, this method returns the contents of the next line in the cache.
read() - returns all the rest
To get everything returned by a subroutine, you can usually expect something and then get it that way, but this approach has a caveat: you have to expect some characters before you can use it to get the rest of the contents of the cache.
read() is used very differently in that it expects an EOF signal and then returns all output up to that signal, as if reading a file.
In general, interactive programs return EOF only when they are closed, such as shutting down an ftp server with the by command, or closing an ssh connection with the exit command.
This method has a narrower scope, since it can be replaced by the method. Of course, if it is a local command that returns EOF after each execution, it is useful:
process = ('ls –l') output = () print output
Seems a bit boring to do this? But I guess there must be some reason to support this method.
You can set the number of characters to be returned by specifying read(size=-1), if not set or if set to a negative number then all the content is returned, if set to a positive number then the specified number of content is returned, and the content returned is in the form of a string.
readline() - returns one line of output
Returns one line of output, the return includes the last \r\n character.
You can also set readline(size=-1) to specify the number of characters to be returned, the default is negative to return all.
readlines() - returns all the output of the list mode
Returns a list where each element of the list is a line (including the \r\n character).
setecho() - subroutine response mode
Set the way the subroutine responds when it runs. Normally when you send characters to a subroutine, they are displayed on the standard output so you can see what you're sending out, but sometimes we don't need to display them.
Note that it must be set before the character is sent, and after it is set it stays in effect in all subsequent code. For example:
process = ('cat') # By default, the following string 1234 is displayed twice, once as returned by pexpect and once as returned by the cat command ("1234") # Now let's turn off the echo function of pexpect() (False) # The following character will only be displayed once, which is returned by cat ("abcd") # Now re-enable echo and you can see the characters we sent again (True)
setwinsize() - console window size
If the subroutine is a console (TTY), for example, SSH connections, Telnet connections such as logging on to the system over the network and sending commands are considered consoles, then you can use this method to set the size (or length and width) of this control too.
It is called by (r, c)
The default value is setwinsize(24, 80), where 24 is the height in lines and 80 is the width in characters.
Why use it? Imagine the following scenario:
Sometimes when you log in to a ssh console via pexpect, and then use interact() to give control to the user, and then the user goes to the console and writes his/her own commands, if the commands are quite long, you will find that the commands don't line feed when they get to the edge of the screen, but instead go back to the very top of the line and overwrite the previous characters; this doesn't affect the actual effect of the command, but it is annoying. This does not affect the actual effect of the command, but it is annoying.
This situation can be solved with setwinsize(), find the length supported by your terminal and reset it, for example, setwinsize(25, 96 ), if it is set correctly it can be solved.
wait() - performs a wait
The program stops (or waits for) execution until the called subroutine has finished. It does not read anything from the called subroutine.
waitnoecho()
It is used in a special place, and the only place it matches is: when the echo function of a subroutine is set to Fals.
Seem strange? Actually, this feature is based on a very mind-boggling but indeed true situation:
In command line mode, in many places where you are asked to enter a password, such as FTP/SSH etc., the password will actually be echoed back to you and printed out, but why can't we see the password we entered? This is because the echo function is set to False by the program before the password is printed out.
Now you know why there is such a method? For example, when you want to make an ssh connection, how do you check if you want to enter a password? Using the keyword password is one way, but there is another way to do just that:
# Start the ssh connection process = ("ssh user@") # Wait for echo to be set to False, which means there will be no echo locally () ('mypassword')
You can set the timeout, the default is: waitnoecho(timeout=-1), which means the same as the timeout set by the system, or you can set it to None, which means you can wait forever until the display is set to False, and of course, you can also set other numbers to indicate the timeout.
write() - sends a string
Similar to the send() command, except that it does not return the number of characters sent.
writelines() - sends a list containing strings
Similar to the write() command, except that it accepts a list of strings. writelines() sends the elements of the list to the subroutine one by one, but does not automatically append a carriage return line feed to the end of each element.
Similar to write(), this method does not return the number of characters sent.
special variable
- Matching termination signals
The EOF variable is used in a variety of ways, such as checking if a ssh/ftp/telnet connection has terminated, or if a file has reached the end. pexpect checks the EOF variable at the end of most of its scripts to see if they are terminating and exiting properly, such as the following code:
("ftp>") ("by") () print "ftp connect terminated."
- Matching timeout signals
The TIMEOUT variable is used to match the timeout, by default expect has a timeout of 60 seconds, if the expected keyword is not found for more than 60 seconds, this behavior will be triggered, for example:
# Matched actions that are only valid if a timeout event occurs index = (['ftp>', ],) if index == 1: () ; # Give control to the user elif index == 2: print "Time is out." (0) ; # Kill the process # So how do you change the timeout? You can actually change the timeout parameter in the spawn object: # The following example simply adds a line so that the timeout is changed = 300 ; # Pay attention to this line index = (['ftp>', ],) if index == 1: () ; # Give control to the user elif index == 2: print "Time is out." (0) ; # Kill the process
/after/match - get the output of a running program
When the expect() procedure matches a keyword (or regular expression), the system automatically assigns values to three variables: before, after and match.
- stores all the data that has been in the cache up to the point where the keyword was matched. That is, if the keyword is finally matched when the cache has 100 characters in it, then before is all the characters except the keyword that was matched.
- Stores the matched keywords, for example, if you use a regular expression inside expect, then all the characters matched by the expression are inside after
- holds an instance of the matched regular expression, as opposed to the after above, where one is the matched string and the other is an instance of the matched regular expression
If an error occurs during expect(), then before saves all the data in the cache up to the current location, and after and match are both None.
|
The above two values are used to save the exit status of the spawn subroutine, but note that these two parameters are only set if the () command is used.
Other notes
CR/LF Conventions
What is well known is this: there are many kinds of carriage return line feed conventions in the world, and they cause us a lot of trouble, for example:
In windows, \r\n is used to indicate a carriage return and line feed.
Linux like systems use \r to indicate a carriage return and line feed.
Mac systems use \n to indicate a carriage return and line feed.
These kinds of carriage return line feed conventions make code porting very difficult, and almost all fully platform-supported programming languages have their own solutions, and pexpect's solution is:
Replace carriage returns with line feeds regardless of platform \r\n
So that's what we have to do if we want to match the carriage return line break number in inspect:
('\r\n') # Trying to match the last word in a line: ('\w+\r\n') # The following match is wrong (it's correct in other scripting languages, such as the Expect implementation of the TCL language, which is where it's easy to get confused): ('\r')
$ * + convention
In regular expressions, the $ symbol means match from the end of a line - but it is not valid in pexpect. To match at the end of a line, there must be a line of data, i.e., a carriage return line feed, but pexpect doesn't process on a line-by-line basis; it reads and processes one character at a time, and doesn't process [future] data.
So whatever you do, don't use the $ symbol to match in expect().
Because pexpect only processes one character at a time, the functions of the plus sign (+) and asterisk (*) are also invalidated, for example:
# Only one character will be returned whenever (".+") # Whenever it returns only null characters (".*")
program debugging
If you want to debug pexpect, then you can use the following:
str(processHandle) # With () you can create a process and control the program by manipulating the handle of this process. # But what if this handle was overloaded with the str() function? It would display a bunch of internal information about this control handle, like: process = ("ftp sw-tftp") print str(process) # < object at 0x2841cacc> version: 2.3 ($Revision: 399 $) command: /usr/bin/ftp args: ['/usr/bin/ftp', 'sw-tftp'] searcher: searcher_re: 0: EOF buffer (last 100 chars): before (last 100 chars): was 14494 bytes in 1 transfers. 221-Thank you for using the FTP service on sw-tftp. 221 Goodbye. after: <class ''> match: <class ''> match_index: 0 exitstatus: 0 flag_eof: True pid: 50733 child_fd: 3 closed: False timeout: 30 delimiter: <class ''> logfile: None logfile_read: None logfile_send: None maxread: 2000 ignorecase: False searchwindowsize: None delaybeforesend: 0.05 delayafterclose: 0.1 delayafterterminate: 0.1
Tips and Traps
circular matching
Python's pexpect module has some features that are significantly under-supported compared to TCL's expect, including loop matching. TCL's expect module gives you a list of matching keywords, and then sets up the continue statement to ensure that the same expect can recycle through the list of keywords.
For example, if an expect has 3 keywords, and the second keyword is matched with a continue statement, then the expect process is repeated for the next match, which is a useful feature, for example, if the timeout is set to 10 seconds, and then repeated 3 times before the timeout actually runs out.
Unfortunately, Python's pexpect doesn't have this capability. But it's not impossible to simulate this, and it can be done with a while statement, for example:
while True: index = ([ , , ]} if index == 0: print "time is out" # Re-match from the beginning continue elif index == 1: print "Terminate." # Terminate the loop break
Strategies for getting the contents of before and emptying the buffer
Most of the time we use the before variable to get the result of a command execution. But do you really know how to use before?
When exactly do you save what you need in before? This detail must be very clear, let's take a call to a command as an example:
Here we are expecting a linux system, where handle is a spawn handle and prompt is the bash prompt. We expect to do steps like this:
Match the prompt to determine that the system is ready to receive commands
Send command
Get the result of the command execution
(prompt) ("ls –l") (prompt) output =
That's 4 statements to get the results of the ls -l command, but wait, is there anything that doesn't make sense?
The first and second sentences match the system prompt and send commands, respectively, which are both relatively normal.
But why does the third sentence match the system prompt again? Under normal circumstances, one would think that after sending a command, the device would execute it and return the results, so it would be perfectly possible to use a statement to get the contents?
In fact, as you can probably tell from the word before, it doesn't work in real time; what's in it is what's left in the system cache after the last expect match, minus the matched keyword itself. That is to say, if the third sentence is output =, then its content is what is left in the cache after the prompt is removed from the first sentence of the expect. Obviously, this will not include the contents of the ls -l command.
Then the only way to get the contents of ls -l is to add another expect keyword match. This is a very critical point.
In addition, the buffer in pexpect is a key, but not directly manipulable, variable that holds everything after each expect in the runtime, and is updated at any time. The before/after are directly derived from it, and the expect keyword matching itself is done in the buffer.
Because of its importance, the contents of this variable require special vigilance. For example, when we write the 3 steps of logging in to the device, sending commands, and logging out of the device into 3 functions, it is best to make sure that each step does not affect the next, and it is best to do this at the beginning of each step:
= ""
code example
FTP server login
The following code is relatively simple, is to log on to an FTP server, and automatically enter the password, and so on into the server, first enter a few predefined commands, and then return control to the user, the user operation is complete, press <kbd>ctrl+]</kbd> that their own operation is complete, the script and then automatically exit the ftp login.
#!/usr/bin/env python import sys import pexpect # Standard prompt for FTP server ftpPrompt = 'ftp>' # Start the FTP server and put all the output from the runtime into the standard output process = ('ftp sw-tftp') process.logfile_read = # Server login process ('[Nn]ame') ('dev') ('[Pp]assword') ('abcd1234') # Automatically enter some predefined commands first cmdList = ("passive", 'hash') for cmd in cmdList: (ftpPrompt) (cmd) (ftpPrompt) # Give FTP control back to the user here, press ctrl+] when the user finishes typing and then give control back to the script # ctrl+] Surrendering control to the script is the default, and the user can set other values such as '\x2a' # is what the user hands back when he presses the asterisk. This value is actually ASCII hexadecimal code, and they correspond to each other # You can look it up yourself elsewhere, but note that it must be in hexadecimal and prefixed with \x () # When the user hands control back to the script, then the script logs out of the ftp server # Note the empty sendline() command below, it's important. When the user hands control back to the script, the # There's nothing in the script cache, so it's not possible to match it. Sending a carriage return here will get it from the server. # Something so it matches. # The final EOF is a way to confirm that the FTP connection is complete. () (ftpPrompt) ('by') ()
The above script actually lacks a lot of error handling, such as wrong username or password after login, or not being able to connect to the server, but the core action is complete.
Above this explanation of the use of the Python Pexpect module is all I have to share with you, I hope to give you a reference, and I hope you will support me more.