【问题标题】:How to control an interactive ssh session with Python's subprocess如何使用 Python 的子进程控制交互式 ssh 会话
【发布时间】:2020-04-23 18:10:58
【问题描述】:

我正在尝试从 python 控制 interactive ssh 会话 - 所以不运行 ssh user@host command(有很多答案)但 ssh user@host 然后写入/读取该 ssh来自 Python 的会话。

我尝试过的总是以:

import subprocess as sp
import sys

ssh_cmd = ["ssh", "-tt", "whoever@myhost"]
proc = sp.Popen(ssh_cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE)
while True:
    stdin = raw_input('enter command: ')
    proc.stdin.write(b'%s\n' % stdin)

问题是如何读取proc.stdout:

  • proc.stdout.readlines() 不起作用,因为它从不返回 EOF,所以它会阻塞
  • proc.stdout.readline() 在有一行数据准备好的情况下有效,但 proc.stdout 在为空时不返回 '' 因此无法读取它,如果没有数据则阻塞
  • select 和 poll 总是说 proc.stdout 已经“准备好”阅读,无论 proc.stdout.readline() 是否真的有效。

有什么建议吗?

编辑:这里的代码是 python2.7,但 python3 的解决方案也可以。 编辑:在这里使用proc.communicate() 不是一个选项,因为它等待进程完成,我想继​​续读/写它。

【问题讨论】:

标签: python python-2.7 ssh


【解决方案1】:

嗯,Marcin Orlowski 的建议肯定是不费吹灰之力,尽管我觉得我没有了解原版的问题所在!

from paramiko.client import SSHClient

client = SSHClient()
client.load_system_host_keys()
client.connect("myhost", username="whoever")
while True:
    cmd = raw_input('enter command: ')
    stdin, stdout, stderr = client.exec_command(cmd)
    print ''.join(stdout.readlines())

【讨论】:

    【解决方案2】:

    要回答代码“原件有什么问题?”,我想说可能有几件事:

    1. 如果您运行的是 Python 3,那么要将raw_input 返回的字符串转换为字节,您需要使用:stdin.encode() 对其进行编码。 将使用系统默认编码,通常为UTF-8。您使用的表达式b'%s\n' % stdin(其中stdin 是str)应该抛出异常,除非您运行的是Python 2,在这种情况下str 和byte 之间没有真正的区别。我知道的 Python 3 中唯一的方法 raw_input 来自 code.InteractiveConsole 类。我没有看到导入语句,因此这表明您正在运行 Python 2,所以我不确定您为什么要尝试从字符串开始转换为字节。

    2. 真正的问题是你需要调用proc.communicate(),这是将你对proc.stdin所做的所有写入的结果发送到实际进程并检索进程对stdout 和stderr 的输出。在你这样做之前,什么都不会发生。最重要的是subprocess.Popen 不适合用于交互式对话框。它应该用于仅向进程发送一些固定输入,调用communicate(),然后检索结果输出。

    使用subprocess的演示

    import subprocess as sp
    import time
    
    ssh_cmd = ["grep", "age"]
    proc = sp.Popen(ssh_cmd, stdin=sp.PIPE)
    proc.stdin.write(b'age1\n')
    proc.stdin.write(b'age2\n')
    proc.stdin.write(b'age3\n')
    time.sleep(5) # nothing happens for these 5 seconds
    proc.communicate() # now the output will start coming out
    
    
    # second demo use pipes for output:
    ssh_cmd = ["grep", "age"]
    proc = sp.Popen(ssh_cmd, stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE)
    proc.stdin.write(b'age1\n')
    proc.stdin.write(b'age2\n')
    proc.stdin.write(b'age3\n')
    stdout, stderr = proc.communicate() # retrieve outputs form the process
    #print(stdout.decode()) # convert from bytes to string for Python 3
    print stdout # Python 2
    

    打印(5 秒后):

    age1
    age2
    age3
    age1
    age2
    age3
    

    【讨论】:

    • 1.这是使用 python 2.7,抱歉应该标记它。 2. 我不想打电话给proc.communicate(),因为那会等待进程终止——我想继续发送标准输入并从进程接收标准输出。
    • 我的重点是,在您调用proc.communicate() 之前,不会将任何内容发送到您的进程,因此这就是为什么您使用Popen 进行交互式会话注定要失败的原因。你说(我逐字引用),“我不觉得我已经了解了原版的问题所在!”所以我只是想通过演示来回答这个问题。
    • 但是proc.stdin.write() 显然确实 发送到进程而不必调用proc.communicate(),好像进程的标准输出是多行的,那么proc.stdout.readline() 将显示第一行输出。只是反复调用显然没有正常的文件语义。我在这里误会你了吗??
    • 没有。我的第一个演示是使用grep age 命令返回包含age 的行。然后它立即向 grep 写入 3 行输入,然后在调用 proc.communicate() 之前等待 5 秒。如果这三行立即转到grep,我希望看到输出,它没有被管道,立即显示。但实际上直到 5 秒过去后才输出,我终于调用了proc.communicate()。如果您有 grep 命令,为什么不尝试运行演示?
    • 来自 Python 手册中有关 communicate 方法的内容:与进程交互:将数据发送到标准输入。从 stdout 和 stderr 读取数据,直到到达文件结尾。 这不是交互式的。见:docs.python.org/3/library/…
    猜你喜欢
    • 1970-01-01
    • 2011-05-05
    • 2017-06-17
    • 1970-01-01
    • 1970-01-01
    • 2015-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多