我编写了以下python模块来处理程序中的ssh连接:

#!/usr/bin/env python
from vxpty import VX_PTY

class SSHError(Exception):
  def __init__(self, msg):
    self.msg = msg
  def __str__(self):
    return repr(self.msg)

class SSHShell:
  def __init__(self, host, port, user, password):
    self.host = host
    self.port = port
    self.user = user
    self.password = password
    self.authenticated = False
  def authenticate(self):
    self.tty = VX_PTY(['/usr/bin/ssh', 'ssh', '-p'+str(self.port), self.user+'@'+self.host])
    resp = self.tty.read()
    if "authenticity of host" in resp:
      self.tty.println('yes')
      while 1:
        resp = self.tty.read()
        if "added" in resp:
          break
      resp = self.tty.read()
    if "assword:" in resp:
      self.tty.println(self.password)
      tmp_resp = self.tty.read()
      tmp_resp += self.tty.read()
      if "denied" in tmp_resp or "assword:" in tmp_resp:
        raise(SSHError("Authentication failed"))
      else:
        self.authenticated = True
        self.tty.println("PS1=''")
    return self.authenticated
  def execute(self, os_cmd):
    self.tty.println(os_cmd)
    resp_buf = self.tty.read().replace(os_cmd+'\r\n', '')
    return resp_buf

它使用了我之前编写的pty模块:

#!/usr/bin/env python
import os,pty

class PTYError(Exception):
  def __init__(self, msg):
    self.msg = msg
  def __str__(self):
    return repr(self.msg)

class VX_PTY:
  def __init__(self, execlp_args):
    self.execlp_args = execlp_args
    self.pty_execlp(execlp_args)
  def pty_execlp(self, execlp_args):
    (self.pid, self.f) = pty.fork()
    if self.pid==0:
      os.execlp(*execlp_args)
    elif self.pid<0:
      raise(PTYError("Failed to fork pty"))
  def read(self):
    data = None
    try:
      data = os.read(self.f, 1024)
    except Exception:
      raise(PTYError("Read failed"))
    return data
  def write(self, data):
    try:
      os.write(self.f, data)
    except Exception:
      raise(PTYError("Write failed"))
  def fsync(self):
    os.fsync(self.f)
  def seek_end(self):
    os.lseek(self.f, os.SEEK_END, os.SEEK_CUR)
  def println(self, ln):
    self.write(ln+'\n')

但是,每当我调用execute()方法时,我最终都会从第一行读取输出:

>>> import SSH;shell=SSH.SSHShell('localhost',22,'735tesla','notmypassword');shell.authenticate()
True
>>> shell.execute('whoami')
"\x1b[?1034hLaptop:~ 735Tesla$ PS1=''\r\n"
>>>

然后第二次调用read()我得到输出:

>>> shell.tty.read()
'whoami\r\n735Tesla\r\n'
>>> 

从输出中删除whoami\\r\\n没问题,但是有什么方法可以清除输出,因此我不必用第一个命令调用两次read?

===============>>#1 票数:1 已采纳

我认为您的问题比您意识到的要深。 幸运的是,它比您意识到的要容易解决。

您似乎想要的是os.read在一次调用中返回shell必须发送给您的全部内容。 那不是你所要求的。 根据几个因素,包括但不限于外壳的实现,网络带宽和延迟以及PTY的行为(您和远程主机的行为),您可以在每次read调用中获取的数据量可以尽可能多的东西,最少只有一个字符。

如果您只想接收命令的输出,则应使用唯一的标记将其括起来,而不必担心与PS1混淆。 我的意思是,您需要在命令执行之前使shell输出一个唯一的字符串,而在命令执行之后需要使外壳输出一个唯一的字符串。 然后,您的tty.read方法应该返回它在这两个标记字符串之间找到的所有文本。 使shell输出这些唯一字符串的最简单方法是使用echo命令。

对于多行命令,必须将命令包装在shell函数中,并在执行函数之前和之后回显标记。

一个简单的实现如下:

def execute(self, cmd):
    if '\n' in cmd:
        self.pty.println(
            '__cmd_func__(){\n%s\n' % cmd +
            '}; echo __"cmd_start"__; __cmd_func__; echo __"cmd_end"__; unset -f __cmd_func__'
        )
    else:
        self.pty.println('echo __"cmd_start"__; %s; echo __"cmd_end"__' % cmd)

    resp = ''
    while not '__cmd_start__\r\n' in resp:
        resp += self.pty.read()

    resp = resp[resp.find('__cmd_start__\r\n') + 15:] # 15 == len('__cmd_start__\r\n')

    while not '_cmd_end__' in resp:
        resp += self.pty.read()

    return resp[:resp.find('__cmd_end__')]

  ask by 735Tesla translate from so

未解决问题?本站智能推荐:

2回复

如果分配了伪tty,为什么在ssh上运行后台任务会失败?

在ssh上运行命令时,我最近遇到了一些奇怪的行为。 我有兴趣听到下面这种行为的任何解释。 运行ssh localhost 'touch foobar &'按预期创建一个名为foobar的文件: 但是,运行相同的命令但使用-t选项强制伪tty分配无法创建foobar :
1回复

ssh如何从tty接收密码?

我想知道openssh是如何在登录时获取密码的,因为我被困在自动向Linux中类似工具输入密码的过程中,这需要从tty像ssh这样获取密码。 试图了解sshpass并发现sshpass派生具有相同pid的子进程,然后在该子进程下输入密码。 不知道我的猜测是否正确,ssh需要检查正确的
1回复

ssh连接关闭时,SIGHUP的原始发送者是谁?

众所周知,当ssh连接断开时,bash将收到一个SIGHUP,并将此信号转发给它的所有子级。 我想知道谁是此SIGHUP的原始发件人,是ssh客户端,ssh服务器,操作系统还是其他? 我阅读了openssh-portabal的代码,发现仅在这里使用了SIGHUP: https : /
2回复

Python读写tty

背景:如果需要,请跳至问题部分 我正在研究测试设备的前端。 前端的目的是使编写长测试脚本更容易。 几乎只是让它们更具人性化和可写性。 该设备将使用Prologix GPIB-USB控制器进行测试(参见prologix.biz)。 我们在http://heliosoph.mit-
1回复

在ssh会话中启用tty

我会接受一些写入的脚本的登录信息,以供许多用户使用。 在python中,我将input_raw设置为从dev / tty读取,但是当我连接到通过ssh在服务器上运行的脚本时,它会失败。 思考? 解决方法? 我宁愿避免将用户名硬编码到脚本中。 谢谢,麻烦您了。
2回复

使用没有RSA密钥的python ssh

Stackoverflow的新手,所以首先,您好。 我正在为我的学校开发一个小项目,该项目应该是开源Unison程序的自定义gui(因为我从未使用过python,所以用python编写对我来说是一个教育挑战)。 我们正在尝试通过尽可能少的输入启动该程序,以允许学生和教职员工在家庭和学校中
1回复

Python + SSH密码验证(没有外部库或公钥/私钥)?

所以我知道你可以使用Pexpect来解决这个问题,但我不想依赖额外的库来解决除Python3提供的问题之外的问题。 我也知道生成公钥并允许它们在远程主机上是理想的,但这就是我打算使用这个脚本(在正确的位置设置键等)。 这是因为我在SO社区的帮助下获得了“我自己的”。 如果分叉的子伪
1回复

Python子进程.Popen管道IO意外阻塞

我正在尝试使用subprocess.Popen来控制ssh进程并通过管道与之交互,如下所示: 如果没有ssh的“ -tt”选项,这可以正常工作。 但是,如果没有分配伪tty,我需要在ssh的远程与之交互的程序会中断,因此我不得不使用它。 这似乎是要进行p.communicate的
2回复

python pty模块-缓冲区挂起?

我正在尝试制作一个小的模块,该模块打开ssh连接并允许通过ssh发送命令并返回命令的输出。 因此,只有一个ssh连接(这样,多个命令就不会导致自动阻止)。 我知道paramiko,但是由于其他限制,我不能使用它,因为必须使用python3.2。 我写的模块在这里: http : //k
6回复

对Docker -t选项分配伪TTY感到困惑

此选项的作用是什么? 我已经阅读了很多有关TTY的文章,但仍然感到困惑。 我试过没有-t和-i ,似乎期望用户输入的程序在没有-t情况下抛出错误。 启用伪TTY为何很重要?