Python - 处理子进程中的输入提示

Python - Dealing with Input Prompt in a Subprocesses

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

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

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”,但 none 导致子进程继续执行(尽管所有 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,因为我是 运行 scp 使用 "os.system(cmdString)" 阻塞了我的线程并迫使我处理这个问题。现在我正在使用子进程,只是触发命令并让它成功或失败是否不好?失败的子进程最终会消失吗,或者我最终会在我有数十或数百个隐藏的 scp 尝试 运行 但等待用户输入的地方结束?

谢谢!

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

你可以在 Whosebug 上搜索类似 scp input 的内容,找到很多类似的问题以及处理方法。

只有当父进程"piped"输出(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')