【问题标题】:Python - Dealing with Input Prompt in a SubprocessesPython - 处理子进程中的输入提示
【发布时间】:2016-08-12 13:43:50
【问题描述】:

我正在尝试在远程部署的嵌入式 Linux 设备上获取 python 脚本来执行 scp 命令。执行命令很简单,但如果目标服务器未列在 'known_hosts' 文件中,scp 会抛出需要与之交互的警告。几天来一直在努力解决这个问题,但我无法解决两个问题。

首先,我无法以非阻塞方式读取来自子进程的响应以正常运行。在以下代码中,即使我知道可以从 stderr 读取(假设生成了受信任的主机文件警告),select 也始终返回 ( [ ], [ ], [ ] )。

cmdString = 'scp user@remote.com:file localFile -i ~/.ssh/id_rsa'

process = subprocess.Popen(shlex.split(cmdString), shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

while(process.poll() is None):
  readable, writable, exceptional = select.select([process.stdout], [], [process.stderr], 1)

  if not (readable or writable or exceptional):
    # Always hits this condition, although adding an "os.read(...)" here
    # will return the error prompt from process.stderr.
    print "timeout condition"
  else:
    # Never makes it here
    for e in exceptional:
      stderr = os.read(process.stderr.fileno(), 256)
      print stderr
    for r in readable:
      stdout = os.read(process.stdout.fileno(), 256)
      print stdout

其次,通过输入 PIPE 提供输入,我无法让子流程超出警告。以下代码从 process.stderr 读取警告代码,但随后挂起,直到我在终端中点击 {enter}。我尝试发送“n”、“n\n”和“\n”,但没有一个会导致子进程继续执行(尽管手动输入时所有 3 种模式都有效)。

cmdString = 'scp user@remote.com:file localFile -i ~/.ssh/id_rsa'

process = subprocess.Popen(shlex.split(cmdString), shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

# Correctly grabs warning and displays it
stderr = os.read(process.stderr.fileno(), 256)
print stderr

# Just in case there was some weird race condition or something
time.sleep(0.5)

# Doesn't ever seem to do anything
process.stdin.write('\n')

最后,重要吗?我最初开始调查子进程和 PIPES,因为我使用“os.system(cmdString)”运行 scp,它阻塞了我的线程并迫使我处理这个问题。现在我正在使用子进程,只是触发命令并让它成功或失败是不是很糟糕?失败的子进程最终会消失吗,或者我最终会在运行了数十或数百个隐藏的 scp 尝试但等待用户输入的情况下结束?

谢谢!

【问题讨论】:

    标签: python pipe subprocess popen


    【解决方案1】:

    这个问题很可能是scp在这种情况下没有使用stdin/stdout/stderr进行通信,而是直接通过终端进行通信。

    你可以通过在stackoverflow上搜索scp input之类的东西找到很多类似的问题以及处理方法。

    只有当父进程“管道”输出(stdout/stderr)并且子进程试图写一些东西时,启动的子进程才会终止。在这种情况下, scp 可能会继续运行,因为它正在使用终端。但是,这些过程并没有真正隐藏。您可以使用ps 之类的工具轻松查看它们(并使用killkillall 杀死它们)。

    编辑:正如您提到的,您在使用各种库时遇到问题,也许以下方法会有所帮助:

    import os, pty
    
    pid, fd = pty.fork()
    if pid == 0:
      os.execvp('scp', ['scp', 'user@remote.com:file', ... ])
    else:
      while True:
        s = os.read(fd, 1024)
        print repr(s)
        os.write(fd, 'something\n')
    

    【讨论】:

    • 谢谢,它不使用标准 stdin/stdout/stderr 的洞察力帮助很大。看起来“pexpect”可以正确驱动 scp,甚至还有一个用于 paramiko 的 scp 插件将它全部带入 python 世界。不幸的是,“pexpect”无法为我创建它需要的虚拟 pty,而且我没有 paramiko 所需的密码学包的编译版本。由于我正在开发简单的第 3 方嵌入式设备,因此更改系统设置以允许创建 pty 或交叉编译包是很困难的。
    • @digitalosmosis 添加了一个更“简单”的示例,可能会对您有所帮助。
    猜你喜欢
    • 2016-02-02
    • 2022-01-04
    • 2014-11-10
    • 1970-01-01
    • 2011-02-08
    • 2021-05-15
    • 1970-01-01
    • 1970-01-01
    • 2014-07-26
    相关资源
    最近更新 更多