python - 从 asyncio 子进程获取实时输出
问题描述
我正在尝试使用 Python asyncio 子进程来启动交互式 SSH 会话并自动输入密码。实际用例并不重要,但它有助于说明我的问题。这是我的代码:
proc = await asyncio.create_subprocess_exec(
'ssh', 'user@127.0.0.1',
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.STDOUT,
stdin=asyncio.subprocess.PIPE,
)
# This loop could be replaced by async for, I imagine
while True:
buf = await proc.stdout.read()
if not buf:
break
print(f'stdout: { buf }')
我希望它能够像 asyncio 流一样工作,我可以在其中创建两个任务/子例程/未来,一个用于监听StreamReader
(在本例中由 给出proc.stdout
),另一个用于写入 StreamWriter(proc.stdin
)。
但是,它没有按预期工作。ssh 命令的前几行输出直接打印到终端,直到它到达密码提示符(或主机密钥提示符,视情况而定)并等待手动输入。我希望能够阅读前几行,检查它是否要求输入密码或主机提示,并相应地写入 StreamReader。
它运行该行的唯一时间print(f'stdout: { buf }')
是在我按下回车键之后,当它打印时,显然,“stderr: b'Host key verification failed.\r\n'”。
我也尝试了推荐的proc.communicate()
,它不像使用 StreamReader/Writer 那样简洁,但它有同样的问题:等待手动输入时执行冻结。
这实际上应该如何工作?如果不是我想象的那样,为什么不呢?有没有办法在不使用线程中的某种繁忙循环的情况下实现这一点?
PS:为了清楚起见,我正在解释使用 ssh。我最终使用 plink 来满足我的需求,但我想了解如何使用 python 来运行任意命令。
解决方案
这不是 asyncio 特有的问题。该ssh
进程不与stdin
和stdout
流交互,而是直接访问TTY 设备,以确保密码输入得到适当保护。
您可以通过三个选项来解决此问题:
不要使用
ssh
,而是其他一些 SSH 客户端,它不希望 TTY 控制。对于 asyncio,您可以使用asyncssh
library。该库直接实现 SSH 协议,因此不需要单独的进程,它直接接受用户名和密码凭据。为 SSH提供一个可以与之通信的伪 tty,这是您的 Python 程序控制的一个。该
pexpect
库提供了一个高级 API,可以为您执行此操作,并可用于完全控制ssh
命令。设置备用密码提示器供 ssh 使用。如果没有 TTY,程序可以通过环境变量
ssh
让其他东西处理密码输入。SSH_ASKPASS
大多数版本ssh
都对何时接受非常挑剔,SSH_ASKPASS
但是,您也需要设置DISPLAY
,使用-n
命令行开关ssh
并使用setsid
命令在新会话中运行ssh
,与任何 TTY 断开连接。
阻力最小的路径是使用 pexpect,因为它原生支持 asyncio(任何接受的方法async_=True
都可以用作协程):
import pexpect
proc = pexpect.spawn('ssh user@127.0.0.1')
await child.expect('password:', timeout=120, async_=True)
child.sendline(password_for_user)
推荐阅读
- c# - 无需手动读取即可访问 `runtimeconfig.json` 内容
- batch-file - 如何创建脚本(.bat)以从 D 驱动器打开文本文件(Windows 10)
- graphql - 使用 TypeORM getManyWithCount 如何为分页生成 PageInfo
- javascript - 没有id时如何获取列表活动元素的索引
- loops - NIFI 迭代日期
- asp.net-identity - 无法使用 cookie 身份验证执行登录
- asp.net-core - 在 Ef Core 中添加对拥有对象的引用
- css - 尝试制作响应式菜单,但 ::before 和 ::after 出现问题
- python-2.7 - 容器化的 celery worker 不会在 docker-compose up 上启动
- java - EntityManagerFactory 在部署耳朵时在 Weblogic 中关闭