江湖道

庙堂,江湖,学术!

返回顶部

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 
posted @ 2024-03-17 15:45  大江东流水  阅读(44)  评论(0编辑  收藏  举报