首页 > 编程语言 >Python 优雅的使用 paramiko 进行交互式输入输出

Python 优雅的使用 paramiko 进行交互式输入输出

时间:2023-08-14 19:24:58浏览次数:40  
标签:commands shell Python self next command 交互式 data paramiko

目的:需要ssh链接到Linux主机,执行telnet 命令,抓回显匹配制定内容。

ssh.exec_command(cmd,bufsize,timeout) #exec_command参数使用只需要执行一次的命令,因为执行完该命令以后,shell会自动回到ssh初始连接的shell状态下

ssh.invoke_shell() #在SSH server端创建一个交互式的shell,且可以按自己的需求配置伪终端,可以在invoke_shell()函数中添加参数配置

也就是command就是发送完指令,连接就会断开
invoke_shell则是长连接,保持状态的那种

import logging
import paramiko
import re
import select
import socket


PROMPT_PATTERN = r'\S+#'

logging.basicConfig(level=logging.DEBUG)
log = logging

paramiko.util.log_to_file('demo.log')

class Channel(object):
    def __init__(self, host, commands, creds=None,
                 prompt_pattern=PROMPT_PATTERN, init_commands=None):
        if creds is None:
            raise RuntimeError('You must supply username and password!!')
        self.host = host
        self.commands = commands

        username, password = creds
        self.creds = creds
        self.username = username
        self.password = password

        self.prompt = re.compile(prompt_pattern)
        if init_commands is None:
            init_commands = []
        self.init_commands = init_commands

        self.results = []

        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

        self.ssh = ssh
        self.initialized = False
        self.record = False

    def run(self):
        """
        This is what we call do actually connect and start the event loop
        """
        # Establish the SSH connection
        self.ssh.connect(self.host, username=self.username,
                         password=self.password)

        # Create an SSH shell descriptor. This is how we're going to interact
        # with the remote device.
        shell = self.ssh.invoke_shell()
        shell.settimeout(0.0)
        self.shell = shell

        # Turn the list of commands into an iterator, so we know when we've run
        # out of commands
        self.cmditer = iter(self.commands)

        # Establish the data buffer we'll use to store output between commands
        self.data = ''

        # And start the event loop to store the results of the commands and
        # return them when we're done.
        results = self.interact()
        return results

    def interact(self):
        """Interact with the device using the SSH shell."""
        shell = self.shell

        # Start an infinite while loop, and use the select.select async I/O
        # handler to detect when data has been received by the SSH shell.
        while True:
            # The ``r`` variable names the object that has received data. See:
            # http://docs.python.org/2/library/select.html#select.select
            r, w, e = select.select([shell], [], [])
            # If 'shell' has received data, try to retreive it:
            if shell in r:
                #log.debug("HEY LET'S DO SOMETHING WITH SHELL")
                try:
                    # Fetch 1K off the socket.
                    bytes = shell.recv(1024).decode('utf-8', 'ignore').strip()

                    # If it's no data, we're done. 
                    if len(bytes) == 0:
                        break
                    
                    # Try to process the data we received.
                    self.data_received(bytes)

                # If we timeout or get an error, log it and carry on.
                except (socket.timeout, socket.error) as err:
                    log.error(str(err))

            # If the socket has not received any data after we sent somethign,
            # disconnect.
            else:
                break

        # The explicit call to disconnect
        shell.close()

        # And finally return the output of the results.
        return self.results

    def data_received(self, bytes):
        """
        This is what we do when data is received from the socket.
        :param bytes:
            Bytes that are received.
        """
        # This is our buffer. Until we get a result we want, we keep appending
        # bytes to the data buffer.

        log.debug('Got bytes: %r' % bytes)
        self.data += bytes
        log.debug(' Buffered: %r' % self.data)

        # Check if the prompt pattern matches. Return None if it doesn't so the
        # event loop continues to try to receive data. 
        # 
        # Basicaly this means:
        # - Loop until the prompt matches
        # - Trim off the prompt
        # - Store the buffer as the result of the last command sent
        # - Zero out the buffer
        # - Rinse/repeat til all commands are sent and results stored
        m = self.prompt.search(self.data)
        if not m:
            return None
        # log.debug('STATE: prompt %r' % m.group())

        # The prompt matched! Strip the prompt from the match result so we get
        # the data received withtout the prompt. This is our result.
        # 
        result = self.data[:m.start()]
        # result = result[result.find('\n')+1:]
        result = result.splitlines()

        # Only keep results once we've started sending commands
        if self.initialized:
            self.results.append(result)

        # And send the next command in the stack.
        self.send_next()

    def send_next(self):
        """
        Send the next command in the command stack.
        """
        # We're sending a new command, so we zero out the data buffer.
        self.data = ''

        # Check if we can safely initialize. This is a chance to do setup, such
        # as turning off console paging, or changing up CLI settings. 
        if not self.initialized:
            if self.init_commands:
                next_init = self.init_commands.pop(0)
                self.shell.send(next_init)
                return None
            else:
                log.debug('Successfully initialized for command execution')
                self.initialized = True

        # Try to fetch the next command in the stack. If we're out of commands,
        # close the channel and disconnect.
        try:
            next_command = next(self.cmditer) # Get the next command
        except StopIteration:
            self.close() # Or disconnect
            return None

        # Try to send the next command
        if next_command is None:
            self.results.append(None) # Store a null command w/ null result
            self.send_next() # Fetch the next command
        else:
            log.debug('sending %r' % next_command)
            self.shell.send(next_command + '\n')  # Send this command

    def close(self):
        """Close the SSH connection."""
        self.ssh.close()

if __name__ == '__main__':
    commands = ['df -h\n', 'top -b -n 1\n']
    host = '192.168.211.100'
    username = 'root'
    password = 'root'
    creds = (username, password)
    prompt_pattern = r'#|\S+ $|\S+ #'
    init_commands = ['dtach -a /tmp/vmm\n', 'su\n']

    c = Channel(host, commands, creds, prompt_pattern, init_commands)
    results = c.run()
    print(results)

标签:commands,shell,Python,self,next,command,交互式,data,paramiko
From: https://www.cnblogs.com/cokefentas/p/17629514.html

相关文章

  • python中function使用class调用和使用对象调用的区别
    问题在python中,class中函数的定义中有一个特殊的self指针,如果一个函数有一个self参数,通常意味着这是一个非静态函数,也就是调用的时候第一个参数是对象指针,只是这个指针是调用这个函数时由python来自动填充。tsecer@harry:catcls_mth.pyclasstsecer():defharry(self):......
  • Python中threading模块 lock、Rlock的使用
    一、概述在使用多线程的应用下,如何保证线程安全,以及线程之间的同步,或者访问共享变量等问题是十分棘手的问题,也是使用多线程下面临的问题,如果处理不好,会带来较严重的后果,使用python多线程中提供Lock、Rlock、Semaphore、Event、Condition用来保证线程之间的同步,后者保证访问......
  • Python中os._exit(), sys.exit(), exit() 的区别
    在Python3.x中,os._exit(),sys.exit(),和exit()是三个不同的终止程序执行的函数,它们之间有一些区别:os._exit():这个函数是os模块中的一个函数,它用于直接终止程序的执行,并且不会触发任何清理活动或关闭程序中的资源。它不会抛出任何异常或执行任何finally子句。使用os._exit(......
  • 各个时区对应的时差表 及 python中时区获取方式(支持夏令时)
    1Africa/Abidjan+00:002Africa/Accra+00:003Africa/Addis_Ababa+03:004Africa/Algiers+01:005Africa/Asmara+03:006Africa/Bamako+00:007Africa/Bangui+01:008Africa/Banjul+00:009Africa/Bissau+00:0010Africa......
  • Python 优雅的使用 subprocess.Popen 获取实时输出,以及中止 subprocess
    #-*-coding:utf-8-*-importshleximportosimportsignalimporttimeimportthreadingfromsubprocessimportPopen,PIPEdefrun_command(command):process=Popen(shlex.split(command),stdout=PIPE)st=time.time()whileTrue:ou......
  • Python合并两个字典代码
    合并两个字典Python3.5之后,合并字典变得容易起来。我们可以通过**符号解压字典,并将多个字典传入{}中,实现合并。defMerge(dict1,dict2):res={**dict1,**dict2}returnres#两个字典dict1={"name":"Joy","age":25}dict2={"name":"Joy",......
  • Python代码链式比较
    链式比较python有链式比较的机制,在一行里支持多种运算符比较。相当于拆分多个逻辑表达式,再进行逻辑与操作。a=5print(2<a<8)print(1==a<3)输出:TrueFalse......
  • 盘点一个列表相加的Python基础题目
    大家好,我是皮皮。一、前言前几天在明佬的Python群【dq】问了一个Python列表基础处理的问题,一起来看看吧。下图是他的原始列表,想通过左边的列表,得到右边的合并列表。二、实现过程这里【流水线】和【hclw】大佬给了一个答案,如下图所示:如此顺利地解决了粉丝的问题。后来他自己也写了个......
  • # yyds干货盘点 # 盘点一个Python自动化办公的实战案例——批量合并Excel文件(上篇)
    大家好,我是皮皮。一、前言前几天在Python星耀群【维哥】问了一个Python自动化办公处理的问题,一起来看看吧。大佬们好,请教一个Python自动化办公的问题,我有一个文件夹,里边有多个Excel文件,分别是员工8月份绩效表格,每一个表格里边都是固定的两列,分别是日期和绩效得分,如下图所示:现在他想......
  • Python学习 -- 常用函数与实例详解
    在Python编程中,数据转换是一项关键任务,它允许我们在不同数据类型之间自由流动,从而提高代码的灵活性和效率。本篇博客将深入探讨常用的数据转换函数,并通过实际案例为你展示如何巧妙地在不同数据类型之间转换。数据类型转换函数Python提供了多种数据类型转换函数,以下是其中几个常用的......