Python 子进程 PIPE 中的多个输入(使用 stdin 或 communicate)

1 投票
3 回答
1571 浏览
提问于 2025-04-18 15:48

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

Popen命令本身不应该返回任何东西,它只是问你“你想继续吗?”(是的),以及从本地数据库获取的“管理员密码”。

目前的代码状态是:一切正常,但它会让我输入第二个内容,第一个输入是可以通过的。

  • 我在使用Ubuntu服务器(用的是Python -u)
  • 尝试在每次stdin.write后刷新
  • 尝试使用communicate(但遇到问题,代码无限循环)
  • 尝试使用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()

3 个回答

0

你可以用 communicate() 来代替 write()

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

它不会显示密码,也不会显示星号。

这意味着你不能通过 stdin 来输入密码。你想做的事情,按照这种方式是行不通的。

看看有没有其他方法可以输入密码——可能是在命令行上,或者通过环境变量,或者干脆不输入密码(比如用 ssh 时,你可以用密钥交换,而不是传递密码)。

如果没有其他办法,你唯一的选择就是给它一个伪终端,而不是普通的管道。(即使这样也不一定能成功,但很可能有效,值得一试。)

在 Python 中,有三种方法可以做到这一点:os.openptyos.forkptypty 模块。理论上,forkpty 是最依赖平台的,但实际上,因为 pty 只在 Linux 上测试过,而 openpty 需要你做其他平台特定的事情才能真正有用,所以我建议你先试试 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) 来等待它。记住,你在处理的是底层的文件输入输出——没有解码成 Unicode,没有通用换行符,最重要的是,没有缓冲。这可能会比较麻烦,但你必须处理这些问题。

当然,你也可以使用像 pexpect 这样的库,它可以使用 PTY,并把所有细节都隐藏起来。我不太明白你为什么对这个主意有抵触,但当你在处理 forkpty 时感到沮丧时,或许可以再考虑一下这个选择。

0

这只是问你“你想继续吗?”(选择是)以及“管理员密码”是什么。

如果子进程问“...继续吗?”你就回答 "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 确保即使子进程直接和终端进行读写(也就是不通过外部进程的输出和输入),你也能看到它的输出,并且它能接收到你的输入。

撰写回答