Python ssh与/交互式程序的连接(例如,python解释器)

时间:2017-01-02 17:18:03

标签: python linux ssh

我想使用python ssh到远程计算机,在远程计算机上启动交互式可执行文件,然后与所述可执行文件进行交互。

换句话说,我想模仿你做类似事情时会发生什么:

ssh me@host -t python2

(python2只是python,但我将其命名为不同以避免混淆)。

当我执行上面的命令行时,我“无缝地”与远程上的python2进行交互。

我似乎无法在python中执行此操作。我尝试过使用paramiko和subprocess,在这一点上都有同样的问题。例如,在子流程中:

ssh = subprocess.Popen("ssh me@host -t python2",
                       shell=True,
                       stdout=subprocess.PIPE,
                       stderr=subprocess.PIPE)
result = ssh.stdout.readlines()
if result == []:
    error = ssh.stderr.readlines()
    print >> sys.stderr, "ERROR: %s" % error
else:
    print result

当我这样做时,我在命令行中看不到任何内容,但我可以通过ps看到python2确实正在运行,我只是无法按照我喜欢的方式与它进行交互。我看不到Python的输出。

我希望能够从主机python脚本提交命令,比如ssh.execute(“1 + 1”)。这基本上会告诉python2计算1 + 1。然后我想将输出读回主机python脚本。

1 个答案:

答案 0 :(得分:2)

问题

您的问题是①ssh.stdout.readlines()正在读取已经缓存到您正在访问它的点的所有现有输入,按行结束(\r\n

但是因为你打开一个提示符,所以②可能只有一行,>>>没有一行结尾。

处理流

您需要做的是创建一个while循环来读取输出:

def handle_output(out):
    while True:
        out_line = out.readline()
        print("OUT: {}".format(out_line))

def handle_errput(err):
    while True:
        err_line = err.readline()
        print("ERR: {}".format(err_line))

那会那样:

handle_output(ssh.stdout)
handle_errput(ssh.stderr)

然后,只要远程python解释器打印出完整的一行,您就会在控制台上显示一些内容。但这只能解决①。

你仍然需要让远程进程输出至少一个完整的行,唯一的方法就是实际向它发送数据!

所以你想添加另一个功能:

def handle_input(inp):
    while True:
        inp_line = sys.stdin.readline()
        inp.write(inp_line)

你会这样称呼:

handle_input(ssh.stdin)

这将解决②。

并行读取

带线程的

但是现在,你有另一个问题,从逻辑上讲,如果你这样做:

handle_output(ssh.stdout)
handle_errput(ssh.stderr)
handle_input(ssh.stdin)

您的计划流程永远不会达到handle_errput()handle_input(),因为它会卡在handle_output()无限循环中。

解决方案是并行执行三个循环:

out_thread = Thread(target=handle_output, args=(ssh.stdout,))
err_thread = Thread(target=handle_errput, args=(ssh.stderr,))
try:
    out_thread.start()
    err_thread.start()
    handle_input()
finally:
    out_thread.join()
    err_thread.join()

这样,从外部进程读取将在另一个线程中发生,并且写入将在当前线程中发生。你也可以启动三个线程(一个用于读取stdout,一个用于读取stderr,一个用于写入)并让主线程运行一个循环,它将执行“填充”并与这些线程通信(提示:使用threading.Queue在线程之间交换数据。)

带有事件库的

(仅限py3)

如果使用异步库like asyncio offered with python 3(对于py2,你可以使用tornado或greenlet),你可以避免使用多线程和线程间通信的痛苦,这将为你提供一个非常好的抽象使用{ {1}}:

select()

使用select()调用

在python 2中,最好也是最简单(但不是最容易实现)的方法是使用select()

基本上,选择调用看起来像

async def handle_output(out):
    while True:
        await out_line = out.readline()
        print("OUT: {}".format(out_line))

async def handle_errput(err):
    while True:
        await err_line = err.readline()
        print("ERR: {}".format(err_line))

async def handle_input(inp):
    while True:
        await inp_line = sys.stdin.readline()
        inp.write(inp_line)

asyncio.Task(handle_output(ssh.stdout))
asyncio.Task(handle_errput(ssh.stderr))
asyncio.Task(handle_input(ssh.stdin))

asyncio.get_event_loop().run_forever()

但你应该更好read a thorough documentation about select()

从哪里去?

  

我希望能够从主机python脚本提交命令,比如ssh.execute(“1 + 1”)。这基本上会告诉python2计算1 + 1。然后我想将输出读回主机python脚本。

我给你的解决方案是帮助你处理从终端到远程python进程的透明通信。它暴露了你实现上述所缺少的所有工具,除了一个,如何在3个循环之间进行通信?

最简单的方法是使用三个队列,两个处理从远程进程传入的结果,最后一个处理传出命令:

while True:
  readable, writable, excepts = select.select(
           [ssh.stdout, ssh.stderr], # outputs to watch
           [sys.stdin],              # inputs to watch
           [ssh.stdout, ssh.stderr], # exceptions to watch
           1)                        # timeout
  for r in readable:
    print(r.read())
  for w in writable:
    p.stdin.write(w.read())

然后在你的主线程中你可以实现:

input_queue = queue.Queue()
output_queue = queue.Queue()
errput_queue = queue.Queue()

并且您的def remote_python_execute(command, output_queue, errput_queue): # send a command to the remote process input_queue.put(command) out, err = ([], []) # read the output and then the errput as long as # there's something to read while not output_queue.empty(): out.append(output_queue.get()) while not errput_queue.empty(): err.append(errput_queue.get()) return out, err 函数看起来像(您可以适应两者:

handle_output

只是为了给你一个想法......现在轮到你填补空白并选择最适合你需求的东西

免责声明:此答案包含未经测试的代码,旨在为您提供如何实现目标的指导。不要复制/粘贴它,而是阅读文档并理解您正在做的事情。还要小心使用线程,作为提示,永远不要在两个线程中使用变量,否则你可能会被咬。硬。

HTH