我想使用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脚本。
答案 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
在线程之间交换数据。)
如果使用异步库like asyncio offered with python 3(对于py2,你可以使用tornado或greenlet),你可以避免使用多线程和线程间通信的痛苦,这将为你提供一个非常好的抽象使用{ {1}}:
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