Python SSH через Pseudo TTY Clear

Я написал следующий модуль 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 из вывода не проблема, но есть ли способ очистить вывод, чтобы мне не приходилось дважды вызывать чтение с помощью первой команды?


person 735Tesla    schedule 20.04.2014    source источник
comment
У меня тоже похожая проблема, но не связанная с ssh   -  person jamespick    schedule 20.04.2014


Ответы (1)


Я думаю, что ваша проблема глубже, чем вы думаете. К счастью, это также легче решить, чем вы думаете.

Кажется, вы хотите, чтобы os.read возвращал все, что оболочка должна отправить вам за один вызов. Это не то, о чем вы можете просить. В зависимости от нескольких факторов, включая, помимо прочего, реализацию оболочки, пропускную способность и задержку сети, а также поведение PTY (вашего и удаленного хоста), объем данных, которые вы получите обратно при каждом вызове read, может быть столько, сколько, ну, все, и всего лишь один персонаж.

Если вы хотите получить только вывод своей команды, вы должны заключить его в уникальные маркеры и не беспокоиться о том, чтобы возиться с PS1. Я имею в виду, что вам нужно заставить оболочку выводить уникальную строку до выполнения вашей команды и еще одну после выполнения вашей команды. Затем ваш метод tty.read должен вернуть весь текст, который он находит между этими двумя строками маркеров. Самый простой способ заставить оболочку выводить эти уникальные строки — просто использовать команду echo.

Для многострочных команд вы должны обернуть команду в функцию оболочки и вывести маркеры до и после выполнения функции.

Простая реализация выглядит следующим образом:

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__')]
person P Daddy    schedule 02.05.2014