ZetCode

Python pexpect

最后修改于 2024 年 1 月 29 日

在本文中,我们将介绍如何使用 pexpect 模块在 Python 中管理交互式程序。

pexpect 模块用于在 Python 中管理交互式程序。它会生成子应用程序,控制它们;并响应其输出中的预期模式。

pexpect 模块用于自动化交互式应用程序,例如 ssh、ftp 或 passwd。它可以用于自动化不同服务器上软件程序安装的设置脚本。

有两个基本函数:runspawn。run 函数执行命令并返回输出。它对于简单的交互很有用。

spawn 函数是一个更强大的选项。它会生成一个外部子命令,然后通过发送行和期望响应来与子命令进行交互。

Python pexpect run

run 函数运行给定的命令;等待它完成;然后将所有输出作为字符串返回。标准错误输出包含在输出中。

main.py
#!/usr/bin/python

import pexpect

r = pexpect.run('echo hello')
print(str(r, 'UTF-8'))

在程序中,我们运行 echo 命令。

r = pexpect.run('echo hello')

我们运行 echo 命令。输出被赋给 r 变量。

print(str(r, 'UTF-8'))

我们使用 str 函数将输出转换为字符串。

$ ./main.py
hello

我们可以使用 bytes.decode 函数将 bytes 转换为 str

main.py
#!/usr/bin/python

import pexpect

r = pexpect.run('pwd').decode("utf-8")

print(f"The current working directory: {r.rstrip()}")

程序运行 pwd 程序,该程序返回当前工作目录。

$ ./main.py
The current working directory: /home/jano/Documents/prog/python/pexpect

Python pexpect logfile

可以将输出重定向到日志文件。run 和 spawn 函数都有 logfile 参数。

main.py
#!/usr/bin/python

import pexpect

f = open('mylog.txt', 'wb')

pexpect.run('ls -l', logfile=f)

f.close()

在程序中,我们运行 ls -l 命令并将输出写入日志文件。

$ ./main.py
$ head -3 mylog.txt
total 60
-rw-rw-r-- 1 jano jano  120 dec 23 10:02 doc.txt
-rwxrwxr-x 1 jano jano  418 dec 23 14:52 ftp2.py

Python pexpect spawn

spawn 函数执行子程序并允许与之交互。

子程序的 expect 函数会扫描流,直到匹配到某个模式。模式是重载的,可以接受多种类型。模式可以是字符串、EOF、编译后的正则表达式或任何这些类型的列表。字符串将被编译为正则表达式类型。

main.py
#!/usr/bin/python

import pexpect

child = pexpect.spawn('date')
child.expect(pexpect.EOF)
output = child.before

print(f'Today is : {output.decode("utf-8")}')
child.close()

在程序中,我们运行 date 命令来获取当前日期时间。

child = pexpect.spawn('date')

我们生成 date 命令。

child.expect(pexpect.EOF)

使用 pexpect.EOF,我们期望子程序已退出。在每次调用 expect 后,beforeafter 属性将被设置为子应用程序打印的文本。

output = child.before

before 属性包含到预期模式的所有文本。

$ ./main.py
Today is : Št 29. december 2022, 11:39:30 CET

Python pexpect FTP 示例

在下一个示例中,我们将与 FTP 命令进行交互。

main.py
#!/usr/bin/python

import pexpect
child = pexpect.spawn('ftp ftp.freebsd.org', encoding='utf-8')

child.expect('Name .*: ')
child.sendline('anonymous')
child.expect('Password:')
child.sendline('')
child.expect('ftp> ')
child.sendline('ls /pub/FreeBSD/')
child.expect('ftp> ')

print(child.before)

child.close()

在程序中,我们连接到一个匿名 FTP 服务器并发出 ls 命令。

child = pexpect.spawn('ftp ftp.freebsd.org', encoding='utf-8')

我们生成 ftp 命令。通过此命令,我们连接到 ftp.freebsd.org 服务器。我们还提供了编码。

child.expect('Name .*: ')

从服务器,我们期望一个以 Name 字符串模式开头后跟一些字符的行。请注意,输入是一个正则表达式。

child.sendline('anonymous')

使用 sendline,我们发送登录名。

child.expect('Password:')
child.sendline('')

我们期望密码提示并发送一个空密码,因为我们以匿名方式登录。

child.sendline('ls /pub/FreeBSD/')

我们发出 ls 命令。

print(child.before)

我们打印 ls 命令的输出。

$ ./main.py
ls /pub/FreeBSD/
229 Entering Extended Passive Mode (|||51545|)
150 Here comes the directory listing.
-rw-r--r--    1 ftp      ftp          4259 May 07  2015 README.TXT
-rw-r--r--    1 ftp      ftp            35 Dec 29 10:45 TIMESTAMP
drwxr-xr-x    9 ftp      ftp            10 Dec 29 10:45 development
-rw-r--r--    1 ftp      ftp          2995 Dec 29 10:00 dir.sizes
drwxr-xr-x   22 ftp      ftp            28 Dec 26 23:00 doc
drwxr-xr-x    6 ftp      ftp             6 Nov 10 15:01 ports
drwxr-xr-x   12 ftp      ftp            14 Dec 29 10:45 releases
drwxr-xr-x   12 ftp      ftp            14 Dec 09 20:25 snapshots
226 Directory send OK.

Python pexpect SSH

在下面的示例中,我们将与 ssh 命令进行交互。

对于此示例,我们必须有一个启用密码身份验证的 ssh 帐户。

main.py
#!/usr/bin/python

import pexpect

username = 'user7@192.168.0.25'
passwd = 'passwd'

child = pexpect.spawn(f'ssh {username}')
f = open('mylog.txt', 'wb')
child.logfile = f

child.expect('password:')
child.sendline(passwd)

i = child.expect(['Permission denied', '[#\$] '])

if i == 0:

    print('failed to login. permission denied')
    child.close()

elif i == 1:

    print('connected')

    child.sendline('uname -a')
    child.expect('[#\$] ')
    r = child.before.decode('utf-8')

    child.write('exit')
    child.close()

f.close()

我们连接到本地网络上的计算机,并在主机上发出 uname 命令。

username = 'user7@192.168.0.25'
passwd = 'passwd'

我们提供用户名和密码。

child = pexpect.spawn(f'ssh {username}')

我们使用 ssh 命令登录到主机。

f = open('mylog.txt', 'wb')
child.logfile = f

输出被定向到日志文件。

child.expect('password:')
child.sendline(passwd)

计算机发送一个提示,我们发送密码。

i = child.expect(['Permission denied', '[#\$] '])

我们期望两种结果:要么是权限被拒绝的消息,要么是标准提示。expect 函数返回匹配结果的索引。

elif i == 1:

    print('connected')

    child.sendline('uname -a')
    child.expect('[#\$] ')

    child.write('exit')
    child.close()

如果登录成功,我们发出 uname 命令。稍后,我们发出 exit 命令来终止连接。

来源

Python pexpect 文档

在本文中,我们介绍了 Python pexpect 模块。

作者

我的名字是 Jan Bodnar,我是一名热情的程序员,拥有丰富的编程经验。我自 2007 年以来一直在撰写编程文章。至今,我已撰写了 1,400 多篇文章和 8 本电子书。我在教学编程方面拥有超过十年的经验。

列出所有 Python 教程