Как управлять интерактивным сеансом s sh с подпроцессом Python - PullRequest
0 голосов
/ 18 апреля 2020

Я пытаюсь управлять интерактивным с sh сеансом с python - поэтому не запускаю ssh user@host command (на который есть множество ответов), но ssh user@host затем пишу в / чтение из этого сеанса s sh из 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)

проблема в том, как читать pro c .stdout:

  • proc.stdout.readlines() не работает, так как он никогда не возвращает EOF, поэтому он блокирует
  • proc.stdout.readline() работает, если готова одна строка данных, но proc.stdout не работает t возвращает '', когда пусто, поэтому нет возможности прочитать его, и если нет данных, он блокирует
  • select и poll всегда говорят, что proc.stdout «готов» для чтения, независимо от того, или нет proc.stdout.readline() на самом деле будет работать.

Есть предложения?

Редактировать: код здесь python2 .7, но решения для python3 тоже подойдут. Редактировать: использование proc.communicate() здесь не вариант, так как ожидание завершения процесса sh, и я хочу продолжать чтение / запись в него.

Ответы [ 2 ]

0 голосов
/ 18 апреля 2020

Чтобы ответить на код «Что не так с оригиналом?», Я бы сказал, что может быть несколько вещей:

  1. Если вы используете Python 3, то для преобразования строка, возвращаемая raw_input в байты, необходимо кодировать с помощью: stdin.encode(). Будет использоваться кодировка системы по умолчанию, которая обычно равна UTF-8. Выражение, которое вы использовали, b'%s\n' % stdin, где stdin - это str, должно вызывать исключение, если вы не используете Python 2, и в этом случае нет реальной разницы между str и byte , Единственный метод raw_input в Python 3, который я знаю, из класса 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
0 голосов
/ 18 апреля 2020

Хм, хорошо, предложение Марцина Орловского, конечно, не требует больших усилий, хотя я не чувствую, что узнал, в чем заключалась проблема с оригиналом!

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())
...