2025-07-16 11:09:20 +08:00

96 lines
2.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import asyncio
import asyncssh
import sys
import termios
import tty
import fcntl
import struct
import signal
class SSHInteractiveClient:
def __init__(self, host, username, password=None, port=22, known_hosts=None):
self.host = host
self.username = username
self.password = password
self.port = port
self.known_hosts = known_hosts
self._old_term_attrs = None
self._process = None
def _get_terminal_size(self):
try:
h, w, _, _ = struct.unpack(
'HHHH',
fcntl.ioctl(sys.stdin, termios.TIOCGWINSZ,
struct.pack('HHHH', 0, 0, 0, 0))
)
return h, w
except Exception:
return 24, 80
def _resize_handler(self, signum, frame):
if self._process:
rows, cols = self._get_terminal_size()
self._process.set_terminal_size(rows, cols)
async def _handle_stdin(self):
try:
while True:
ch = await asyncio.get_event_loop().run_in_executor(None, sys.stdin.read, 1)
if ch:
self._process.stdin.write(ch)
except asyncio.CancelledError:
pass
def _enable_raw_mode(self):
self._old_term_attrs = termios.tcgetattr(sys.stdin)
tty.setraw(sys.stdin.fileno())
def _restore_terminal(self):
if self._old_term_attrs:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, self._old_term_attrs)
async def connect_and_run(self):
self._enable_raw_mode()
rows, cols = self._get_terminal_size()
async with asyncssh.connect(
self.host,
port=self.port,
username=self.username,
password=self.password,
known_hosts=self.known_hosts
) as conn:
self._process = await conn.create_process(term_type='xterm', term_size=(rows, cols))
signal.signal(signal.SIGWINCH, self._resize_handler)
stdin_task = asyncio.create_task(self._handle_stdin())
try:
async for line in self._process.stdout:
print(line, end='', flush=True)
except (asyncio.CancelledError, EOFError):
pass
finally:
stdin_task.cancel()
self._restore_terminal()
# 示例使用方法
if __name__ == '__main__':
client = SSHInteractiveClient(
host='180.76.111.84',
username='root',
password='Kyy@123456', # 或设为 None使用 key 登录
known_hosts=None # 不验证 known_hosts
)
try:
asyncio.run(client.connect_and_run())
except (asyncssh.Error, OSError) as e:
print(f'Connection failed: {e}')
except KeyboardInterrupt:
client._restore_terminal()