pexpect手稿
Pexpect是一个Python模块,用于生成子应用程序并自动控制它们。Pexpect可用于自动化交互式应用程序,如ssh、ftp、passwd、telnet等。它可用于自动化在不同服务器上复制软件包安装的设置脚本。它可以用于自动化软件测试。Pexpect是Don Libes的Expect的精神,但Pexpect纯粹是Python。Python的其他类似Expect的模块需要TCL和Expect,或者需要编译C扩展。Pexpect不使用C、Expect或TCL扩展。它应该在任何支持标准Python pty模块的平台上工作。Pexpect界面注重易用性,使简单的任务变得容易。
Pexpect系统有两个主要接口;这些是函数run()和类spawn。spawn类更强大。run()函数比spawn更简单,而且非常适合快速调用程序。当您调用run()函数时,它会执行给定的程序,然后返回输出。这是os.system()的一个方便的替代品。
例如
pexpect.run('s-la')
派生类是Pexpect系统的更强大的接口。您可以使用它生成一个子程序,然后通过发送输入和期望响应(等待子程序输出中的模式)与之交互。
例如
child=pexpect.spown('scp foouser@example.com:.')
child.expect(“密码:”)
child.sedline(mypassword)
上下文管理器可用于spawn()函数::
with pexpect.spawn('scp foo user@example.com:.') as child:
child.expect('Password:')
child.sendline(mypassword)
这甚至适用于要求密码或正常stdio流之外的其他输入的命令。例如,ssh直接从TTY设备读取输入,该设备绕过stdin
匹配模式:
这在流中寻找,直到匹配到模式。该模式已过载,可能有几种类型。该模式可以是StringType、EOF、已编译的re或任何这些类型的列表。
字符串将被编译为重新类型。这会将索引返回到模式列表中。如果模式不是列表,则在成功匹配时返回索引0。这可能会引发EOF或TIMEOUT的异常。为了避免EOF或TIMEOUT异常,请将EOF或TIMEOUT添加到模式列表中。这将导致期望匹配EOF或TIMEOUT条件,而不是引发异常。
如果传递一个模式列表和多个匹配项,则会选择流中的第一个匹配项。如果在该点匹配多个模式,则选择模式列表中最左边的模式。例如
#输入为“foobar”
index=p.expect(['bar','foo','foobar'])
#返回1('fo'),即使'foobar'是“更好”的匹配
但是,请注意,缓冲可能会影响这种行为,因为输入是以不可预测的块到达的。例如
#输入为“foobar”
index=p.expect(['fobar','foo'])
#如果所有输入同时可用,则返回0('fobar'),
#但如果最后一个“bar”的部分延迟到达,则返回1(“o”)
当找到给定模式的匹配项时,类实例属性*match*将变为re.MatchObject结果。如果EOF或TIMEOUT模式匹配,那么match属性将是该异常类的实例。类实例属性之前和之后的配对是匹配模式之前和之后数据的视图。在一般异常中,类属性*before*是在异常之前接收到的所有数据,而*match*和*after*属性的值为None。
当关键字参数timeout为-1(默认值)时,timeout将在类timeout属性指定的默认值之后引发。当None时,TIMEOUT将不会引发,并且可能会无限期地阻止,直到匹配为止。
当关键字参数searchwindowsize为-1(默认值)时,将使用类maxread属性指定的值。
列表条目可以是EOF或TIMEOUT,而不是字符串。这将捕获这些异常并返回列表条目的索引,而不是引发异常。特性“after”将被设置为异常类型。属性“match”将为None。这允许您编写如下代码:
1 index = p.expect(['good', 'bad', pexpect.EOF, pexpect.TIMEOUT]) 2 if index == 0: 3 do_something() 4 elif index == 1: 5 do_something_else() 6 elif index == 2: 7 do_some_other_thing() 8 elif index == 3: 9 do_something_completely_different() 10 11 instead of code like this:: 12 13 try: 14 index = p.expect(['good', 'bad']) 15 if index == 0: 16 do_something() 17 elif index == 1: 18 do_something_else() 19 except EOF: 20 do_some_other_thing() 21 except TIMEOUT: 22 do_something_completely_different()
这两种形式是等价的。这完全取决于你想要什么。如果您正在等待子级的所有输出完成,您也可以只期望EOF。例如
p = pexpect.spawn('/bin/ls')
p.expect(pexpect.EOF)
print p.before
如果您试图优化速度,请参阅expect_list()。
在安装了asyncio的Python 3.4或Python 3.3上,传递“async_=True”将使此返回一个:mod:“asyncio”协程,您可以从中获得与此方法通常直接给出的结果相同的结果。因此,在协同程序中,您可以替换以下代码:
index = p.expect(patterns)
使用这种非阻塞形式:
index = yield from p.expect(patterns, async_=True)
interact
这将子进程的控制权交给了交互式用户(键盘上的人)。将击键发送到子进程,并打印子进程的stdout和stderr输出。这只是将子stdout和子stderr回声到真正的stdout,并将真正的stdin回声到子stdin。当用户键入escape_character时,此方法将返回None。escape_character将不会被传输。escape_character的默认值输入为`Ctrl-]``,与BSD telnet非常相似。为了防止转义,escape_character可以设置为None
如果指定了日志文件,则在交互模式下从子进程发送和接收的数据将复制到给定的日志中
您可以传入可选的输入和输出过滤器函数。这些函数应该采用字节数组,也应该返回字节数组。即使支持`encoding='tf-8'``,meth:`interact`也将始终传递input_filter和output_filter字节。您可能需要包装您的函数以解码并编码回UTF-8。
output_filter将传递来自子进程的所有输出。
input_filter将传递来自用户的所有键盘输入。
input_filter在检查escape_character之前运行。
请注意,如果更改父级的窗口大小,SIGWINCH信号将不会传递给子级。如果希望在父窗口大小更改时更改子窗口大小,请执行以下示例中的操作:
import pexpect, struct, fcntl, termios, signal, sys
def sigwinch_passthrough (sig, data):
s = struct.pack("HHHH", 0, 0, 0, 0)
a = struct.unpack('hhhh', fcntl.ioctl(sys.stdout.fileno(),
termios.TIOCGWINSZ , s))
if not p.closed:
p.setwinsize(a[0],a[1])
# Note this 'p' is global and used in sigwinch_passthrough.
p = pexpect.spawn('/bin/bash')
signal.signal(signal.SIGWINCH, sigwinch_passthrough)
p.interact()
spawn
这是构造函数。命令参数可以是包括命令和该命令的任何参数的字符串。例如
child=pexpect.spown('/usr/bin/fftp')
child=pexpect.spown('/usr/bin/sshuser@example.com')
child=pexpect.spown('ls-latr/tmp')
您也可以使用以下参数列表来构建它:
child=pexpect.spown('/usr/bin/fftp',[])
child=pexpect.spown('/usr/bin/ssh',[]user@example.com'])
child=pexpect.spown('ls',['-latr','/tmp'])
之后,子应用程序将被创建并准备好与之对话。有关正常使用,请参阅expect()、send()和sendline()。
请记住,Pexpect不会解释外壳元字符,如重定向、管道或通配符(``>``、`|``或`*``)。这是一个常见的错误。如果要运行一个命令并通过另一个命令进行管道传输,则还必须启动一个shell。例如
child=pexpect.spown('/bin/bash-c“ls-l|grep LOG>logs.txt”')
child.expect(pexpect.EOF)
第二种形式的派生(传递参数列表)在希望派生命令并传递其自己的参数列表的情况下很有用。这样可以使语法更加清晰。例如,以下等效于上一个示例:
shell_cmd='ls-l|grep LOG>logs.txt'
child=pexpect.spown('/bin/bash',['-c',shell_cmd])
child.expect(pexpect.EOF)
maxread属性设置读取缓冲区的大小。这是Pexpect一次尝试从TTY读取的最大字节数。将maxread大小设置为1将关闭缓冲。在从子级读取大量输出的情况下,将maxread值设置得更高可能有助于提高性能。此功能与searchwindowsize配合使用非常有用。
当关键字参数*searchwindowsize*为None(默认值)时,将在每次接收传入数据的迭代中搜索完整的缓冲区。
在每次迭代时扫描的默认字节数非常大,并且可以减少以辅助降低搜索成本。在:meth:`~.expect`返回后,无论*searchwindowsize*值如何,完整缓冲区属性的大小都保持为*maxread*。
当关键字参数“timeout”被指定为数字(默认值:*30*)时,对于任何一个:meth:`~.expect`方法调用族,在指定的值过去后(以秒为单位),将引发:class:`timeout`。当None时,不会引发TIMEOUT,并且:meth:`~.expect`可能会无限期阻止,直到匹配为止。
日志文件成员打开或关闭日志记录。所有输入和输出都将被复制到给定的文件对象中。将logfile设置为None以停止日志记录。这是默认设置。将日志文件设置为sys.stdout,以将所有内容都回显到标准输出。每次写入后都会刷新日志文件。
文件的日志输入和输出示例:
child=pexpect.spown('some_command')
fout=打开('mylog.txt','b')
child.logfile=fout
到stdout的日志示例::
#在Python 2中:
child=pexpect.spown('some_command')
child.logfile=sys.stdout
#在Python 3中,我们将使用`encoding``参数来解码数据
#从子流程中,并将其作为unicode处理:
child=pexpect.spown('some_command',编码='tf-8')
child.logfile=sys.stdout
logfile_read和logfile_send成员可以用于分别记录来自子级的输入和发送给子级的输出。有时你不想看到你给孩子写的所有东西。您只想记录孩子发回的内容。例如
child=pexpect.spown('some_command')
child.logfile_read=sys.stdout
如果您使用的是Python3,则需要在上面的代码中传递一个编码来派生。
要单独记录发送给子级的输出,请使用logfile_send:
child.logfile_send=fout
如果“ignore_sighup”为True,则子进程将忽略sighup信号。Pexpect 4.0中的默认值为False,这意味着SIGHUP将由子级正常处理。
延迟预测有助于克服许多用户正在经历的一种奇怪行为。典型的问题是,用户会期望()出现“Password:”提示,然后立即调用sendline()发送密码。然后,用户将看到他们的密码被回显给他们。密码通常不会回音。这个问题是由以下事实引起的:大多数应用程序打印出“密码”提示,然后关闭stdin echo,但如果您在应用程序关闭echo之前发送密码,那么您的密码就会得到回音。
通常情况下,当在区域键盘上与人交互时,这不会是一个问题。如果你在写作前引入一个轻微的延迟,那么这似乎可以解决问题。对于许多用户来说,这是一个常见的问题,因此我决定默认的pexpect行为应该是在写入子应用程序之前休眠。1/20秒(50毫秒)似乎足以解决问题。您可以将delaybeforesend设置为None以返回到旧的行为。
请注意,spawn在您的路径上查找命令方面非常聪明。它使用与“which”用于查找可执行文件相同的逻辑。
如果您希望获得子级的退出状态,则必须调用close()方法。子项的退出或信号状态将存储在self.exitstatus或self.signalstatus中。如果孩子正常退出,则exitstatus将存储退出返回代码,信号状态将为None。如果子项被信号异常终止,则signalstatus将存储信号值,exitstatus将为None::
child = pexpect.spawn('some_command')
child.close()
print(child.exitstatus, child.signalstatus)
如果您需要更多详细信息,还可以阅读self.status成员,该成员存储os.waipid返回的状态。你可以使用os来解释这一点。WIFEXITED/os。WEXITSTATUS或os。WIFSIGNALED/os。TERMSIG。
echo属性可以设置为False以禁用输入的回显。作为一个伪终端,所有由“键盘”(send()或sendline())响应的输入都将被重复输出。在许多情况下,不希望启用echo,稍后可能会使用setecho(False)和waitnoecho()禁用它。但是,对于某些平台(如Solaris),这是不可能的,应该在生成时立即禁用。
如果给定presec_fn,则在启动给定命令之前,将在子进程中调用它。这对于例如重置继承的信号处理程序非常有用。
dimensions属性指定子流程所看到的伪终端的大小,并被指定为一个双条目元组(行、列)。如果未指定,则ptyprocess中的默认值将适用。
use_poll属性允许使用select.poll()而不是select.select()进行套接字处理。如果您的系统可能有>1024个fds,这很方便
在distribution tarball目录下,您应该可以找到一个“examples”目录。这是学习使用Pexpect的最好方法。请参阅“预期示例”的说明。
这在本地或远程服务器上运行netstat。它计算一些关于外部inet连接数量的简单统计信息。这可用于检测一个IP地址是否占用了过多的连接。如果给定的IP地址在脚本运行之间超过阈值,它还可以发送电子邮件警报。这个脚本可以用作Munin插件,也可以独立于cron使用。我在繁忙的网络服务器上使用过这个,有时会受到拒绝服务攻击。这样可以很容易地查看脚本是否打开了多个连接。一个典型的浏览器一次打开的连接不到10个。一个脚本可能同时打开100多个连接。
https://pexpect.readthedocs.io/en/latest/examples.html
1 import pexpect, subprocess 2 import sys 3 4 # child = pexpect.spawn('python3 t_pexpect.py', encoding='utf-8', logfile = sys.stdout) 5 # child.expect('_________') 6 # child.sendline('1234567890') 7 8 with pexpect.spawnu('gpg --full-generate-key') as bash: 9 # bash = pexpect.spawnu('gpg --full-generate-key', logfile = sys.stdout) 10 bash.expect("Your selection?") 11 bash.sendline('1') 12 bash.expect("What keysize do you want?") 13 bash.sendline('2048') 14 bash.expect(['valid']) 15 bash.sendline('0') 16 bash.expect("Is this correct?") 17 bash.sendline('y') 18 bash.expect("Real name: ") 19 bash.sendline('testxx') 20 bash.expect("Email address: ") 21 bash.sendline('testxx@123.com') 22 bash.expect("Comment: ") 23 bash.sendline('This is a test') 24 bash.expect(["Change", "Name"]) 25 bash.sendline('O') 26 bash.expect(["Passphrase:"]) 27 bash.delaybeforesend = 10 28 bash.interact(escape_character='x') 29 # bash.sendline('1234567890') 30 bash.expect(["Passphrase:"]) 31 bash.delaybeforesend = 10 32 bash.interact(escape_character='x') 33 # bash.sendline('1234567890')
1 import pexpect, subprocess 2 import sys 3 4 # child = pexpect.spawn('python3 t_pexpect.py', encoding='utf-8', logfile = sys.stdout) 5 # child.expect('_________') 6 # child.sendline('1234567890') 7 8 with pexpect.spawnu('/bin/bash', encoding='utf-8', logfile = sys.stdout) as child: 9 # bash = pexpect.spawnu('gpg --full-generate-key', logfile = sys.stdout) 10 child.expect("# ") 11 child.sendline("python3 pexpect_03.py") 12 code = child.expect(["________________________________________", "<Cancel>", "<OK>"]) 13 # print(">>>>", code) 14 # child.sendline("x") 15 child.writelines(["1234567890x\n"]) 16 code = child.expect(["________________________________________", "<Cancel>", "<OK>"]) 17 child.writelines(["1234567890x\n"])
1 import pexpect, subprocess 2 import sys 3 4 # child = pexpect.spawn('ls', encoding='utf-8', logfile = sys.stdout) 5 # child.expect(pexpect.EOF) 6 # child = pexpect.spawn('/bin/bash', encoding='utf-8', logfile = sys.stdout) 7 # child.expect('# ') 8 # child.sendline("gpg --full-generate-key") 9 # child = pexpect.spawn('gpg --full-generate-key', encoding='utf-8', logfile = sys.stdout) 10 # def bar(data): 11 # print(">>>>>>>>data:", data) 12 13 14 with open("./log.txt", "w") as f: 15 bash = pexpect.spawn('/bin/bash') 16 bash.expect("# ") 17 bash.sendline('gpg --full-generate-key') 18 bash.expect("Your selection?") 19 bash.sendline('1') 20 bash.expect("What keysize do you want?") 21 bash.sendline('2048') 22 bash.expect(['valid']) 23 bash.sendline('0') 24 bash.expect("Is this correct?") 25 bash.sendline('y') 26 bash.expect("Real name: ") 27 bash.sendline('testxx') 28 bash.expect("Email address: ") 29 bash.sendline('testxx@123.com') 30 bash.expect("Comment: ") 31 bash.sendline('This is a test') 32 bash.expect(["Change", "Name"]) 33 bash.sendline('O') 34 bash.expect(["Passphrase:"]) 35 # bash.delaybeforesend() 36 child = bash.interact() 37 38 39 # cmd = 'gpg --full-generate-key' 40 # child = pexpect.spawn('/bin/bash', ['-c', cmd], encoding='utf-8') 41 # child = pexpect.spawn('gpg --full-generate-key', encoding='utf-8', logfile = f) 42 # child.expect("Your selection?") 43 # child.sendline('1') 44 # child.expect("What keysize do you want?") 45 # child.sendline('2048') 46 # child.expect("Key is valid for?") 47 # child.sendline('0') 48 # child.expect("Is this correct?") 49 # child.sendline('y') 50 # child.expect("Real name: ") 51 # child.sendline('testxx') 52 # child.expect("Email address: ") 53 # child.sendline('testxx@123.com') 54 # child.expect("Comment: ") 55 # child.sendline('This is a test') 56 # child.expect(["Change", "Name"]) 57 # child.sendline('O') 58 # child.expect(["Passphrase:"]) 59 # child.interact(escape_character='x') 60 # child.expect(["Passphrase:"]) 61 # child.interact(escape_character='x') 62 # child.writelines(["1234567890"]) 63 # child.sendline('1234567890') 64 # child.expect(["<OK>"]) 65 # child.sendline('exit') 66 # child.send("\n") 67 # index = child.expect([pexpect.EOF, pexpect.TIMEOUT]) 68 # if index == 1: 69 # print("timeout ....") 70 # child.close(force=True) 71 # if index == 0: 72 # print("not cache") 73 # child.close(force=True) 74 75 # child.expect(["Passphrase:"]) 76 # child.interact() 77 # # child.writelines(["1234567890"]) 78 # child.writelines(":wq") 79 # child.send("\n") 80 # index = child.expect([pexpect.EOF, pexpect.TIMEOUT]) 81 # if index == 1: 82 # print("timeout ....") 83 # child.close(force=True) 84 # if index == 0: 85 # print("not cache") 86 # child.close(force=True) 87 88 # child.sendline('1234567890') 89 # child.expect(["<OK>"]) 90 # child.sendline('exit') 91 # child.writelines(":wq") 92 # child.send("\n") 93 # child.expect(pexpect.EOF) 94 # child.before.decode() 95 # child.close() 96 97 98 99 100 101