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

时间:2016-08-12 13:43:50

标签: python pipe subprocess popen

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

首先,我无法从子进程中获取非阻塞读取响应以正常运行。在下面的代码中,select总是返回([],[],[]),即使我知道我可以从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"但没有一个会导致子进程继续执行(尽管所有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尝试的地方结束,但是等待用户输入?

谢谢!

1 个答案:

答案 0 :(得分: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')