【问题标题】:Multiple inputs in a Python Subprocess PIPE (with stdin or communicate)Python 子进程 PIPE 中的多个输入(使用标准输入或通信)
【发布时间】:2023-12-30 05:15:01
【问题描述】:

我一直在寻找上述问题的答案。

Popen 命令不应该返回任何内容,它只是询问“你想继续吗?” (是)和取自本地数据库的“管理员密码”。

当前代码状态是:一切正常,但询问我第二个输入,第一个输入通过。

  • 我使用的是 Ubuntu 服务器(使用 Python -u)
  • 在每次 stdin.write 后尝试刷新
  • 尝试通信(有问题,代码循环无限次)
  • 试过 pexpect 但也有问题,我猜 Popen 更适合我的条件。

所以,我愿意接受建议并愿意尝试您的帮助。任何帮助将不胜感激。

        #Input 1
        p = Popen(["foo", "bar"], stdin=PIPE, universal_newlines=True)
        try:
            p.stdin.write('yes' + linesep)
        except IOError as e:
            if e.errno == errno.EPIPE or e.errno == errno.EINVAL:
                break
            else:
                raise
        p.stdin.flush()
        #Input 2
        try:
            p.stdin.write(KeyPass + linesep)
        except IOError as e:
            if e.errno == errno.EPIPE or e.errno == errno.EINVAL:
                break
            else:
                raise
        p.stdin.flush()
        p.stdin.close()
        p.wait()

【问题讨论】:

  • 如果您正在尝试驱动交互式应用程序,Popen 并不比pexpect 更适合您的条件。向我们展示您尝试了什么以及出了什么问题;几乎可以肯定这是一个更容易解决的问题。
  • 另外,它要求“从本地数据库获取的“管理员密码””。通常,当程序要求输入密码时,它们要么从 TTY 而不是 stdin 读取,要么在 stdin 不是 TTY 或无法设置为 NOECHO 时拒绝继续,或者调用 libpam 或类似名称而不是自己读取.如果您在命令行上运行此命令,输入时是否会回显密码? (此外,您没有读取标准输出,因此在发送密码之前没有等待提示,如果它在提示之前丢弃输入,则会破坏它,许多密码输入都会这样做。)
  • 即使发送的是输入,您也看不到任何东西,但它可以工作,您可以尝试其他任何东西,然后它不会要求管理员密码。我也不想看到任何东西,当我的工作完成时我不会打印输入或输出,我现在正在调试。此外,管理员密码是使用 minidom 从 XML 数据库中解析的。所以它被设置为 KeyPass 变量。我不知道你所说的细节对不起。它是一个简单的应用程序,popen 就足够了。
  • 再次,当您为命令行运行应用程序时,它是否回显密码?如果您在提示符之前开始输入,它是否会忽略您在提示符之前输入的任何内容?
  • 这就像更改*nix系统的密码一样。它既不回显密码也不回显星号。很抱歉在我的第一条评论中造成误解。

标签: python subprocess stdin popen


【解决方案1】:

它既不回显密码也不回显星号。

这意味着您无法驾驶它通过stdin。你试图做的事情是不可能的。

看看是否有其他方法可以给它一个密码——可能是在命令行上,或者通过环境——或者避免给它一个密码(例如,使用ssh,你可以交换密钥而不是传递密码)。

如果没有,您唯一的选择是给它一个伪 TTY 而不是常规管道。 (即使这样也不能保证有效,但它可能有效,值得一试。)

在 Python 中有三种方法可以做到这一点:os.openptyos.forkptypty 模块。理论上,forkpty 是最特定于平台的,但在实践中,因为 pty 仅在 Linux 上进行测试,而 openpty 要求您执行其他特定于平台的操作才能实际使用它做任何有用的事情,我' d 以forkpty 开头。代码如下所示:

pid, fd = os.forkpty()
if not pid:
    # We're in the child here, but we still have to launch the program
    os.execlp('foo', 'bar')
# This code is still in the parent.

现在,家长可以使用os.read(fd)os.write(fd) 与孩子交谈,孩子会看到它来自TTY。当孩子完成后,你必须os.waitpid(pid)它。请记住,您在这里处理的是低级文件 I/O — 没有解码为 Unicode,没有通用换行符,最重要的是,没有缓冲。这可能很难处理,但您必须处理它。

或者,当然,您可以使用像pexpect 这样的库,它可以使用 PTY 并对您隐藏所有详细信息。我不知道你为什么反对这个想法,但是当你在尝试处理 forkpty 时感到沮丧时,不妨再给它一次机会。

【讨论】:

  • 我应该再试一次 pexpect 然后回到这个。
  • 终于在使用 pexpect 几个小时后,我成功了。谢谢你。
【解决方案2】:

它只是询问“你想继续吗?” (是)和“管理员密码”

回答"yes"如果子进程询问“...继续?”并在提示时输入密码:

import pexpect # $ pip install pexpect

output, status = pexpect.run('foo bar',
    events={r'continue\?': 'yes\n', 'password': 'p4$$W__rd\n'},
    withexitstatus=1)

pexpect 确保即使子进程直接向终端写入/读取(外部进程的标准输出/标准输入),您也会看到它的输出并接收您的输入。

【讨论】:

  • 对于那些使用 Windows 的人,run() 将不起作用。它依赖于 Unix 伪终端。从版本 4 开始,documentation 建议使用pexpect.popen_spawn.PopenSpawn(这是实验性的)。
  • @AndrewMyers OP 使用 Ubuntu。 Pexpect 在回答时根本不支持 Windows。如果程序直接从控制台读取密码,PopenSpawn 将无济于事(getwch/ReadConsole API — 使用 pty 的 Windows 模拟)。
【解决方案3】:

您可以使用communicate() 代替write()

p.communicate(input='{}\n{}'.format('yes', KeyPass).encode('utf-8'))

【讨论】:

最近更新 更多