相关文章推荐
安静的领带  ·  shell 和 command模块 · ...·  3 月前    · 
酒量小的领带  ·  es报Unexpected ...·  3 月前    · 
非常酷的消防车  ·  老灰菜网络杂记·  4 月前    · 
正直的火车  ·  jest expect array ...·  5 月前    · 
奔放的包子  ·  Stream.WriteAsync 方法 ...·  1 年前    · 

通过python代码来实现类似expect shell的交互式能力,这样在python代码中能结合业务逻辑组装代码,具有比expect更强大的自动化处理能力。

如果能通过该交互能力,结合SFTP来实现文件相关的操作,那就更加的强大了。

该篇文章代码为交互式命令封装实现。

依赖paramiko库,所以需要执行 pip install paramiko 来安装。

代码封装实现

import time
import paramiko
from paramiko import SSHClient, Channel
import re
recv_len = 1024 * 10
class PtySession(object):
    def __init__(self, session, ending_char, timeout=10) -> None:
        super().__init__()
        self.session = session  # type:Channel
        self.last_line = ""
        self.ending_char = ending_char  # 每执行一个命令之后,标记输出的结束字符
        self.clear_tail()
        self.timeout = timeout  # 超时时间,秒
    def clear_tail(self):
        清理输出还处于缓冲区中未读取的流
        while True:
            time.sleep(0.2)
            # self.session.recv_ready()在读取过程中不一定总是True,只有当读取缓冲流中有字节读取时,才会为True。所以在读取头一次后获取下次流到缓冲区中前为False
            if self.session.recv_ready():
                self.last_line = self.session.recv(recv_len)
                self.last_line = self.last_line.decode('utf-8')
                print(self.last_line, end="")
            if re.search(self.ending_char, self.last_line):
                break
    def destroy(self):
        销毁并关闭session
        :return:
        :rtype:
        self.clear_tail()
        self.session.close()
    def exp(self, *exp_cmds):
        期望并执行,与expect的用法类似。
        :param exp_cmds:
        第一个元素为获取的期望结束字符,第二个元素为需要执行的命令,如果传入的第三个元素,则第三个元素必须为元祖,并且也同父级一样,属递归结构。
        类似GNU的递归缩写。
        :type exp_cmds: tuple
        interval = 0.2
        cur_time = 0.0
        while True:
            if self.session.recv_ready():
                self.last_line = self.session.recv(recv_len).decode('utf-8')
                print(self.last_line, end="")
            elif self.session.send_ready():
                for exp_cmd in exp_cmds:
                    _cmd = exp_cmd[1]
                    if not _cmd.endswith("\r"):
                        _cmd += "\r"
                    match = re.search(exp_cmd[0], self.last_line)
                    if match and match.group():
                        self.session.send(_cmd)
                        # 清空最后一行数据缓存,便于下个命令的读取流输出。此行代码去除,会导致无法等待命令执行完毕提前执行后续代码的问题。
                        self.last_line = ""
                        if len(exp_cmd) == 3 and exp_cmd[2]:
                            self.exp(exp_cmd[2])
                        return
            cur_time += interval
            if cur_time >= self.timeout:
                raise Exception("timeout...")
            time.sleep(interval)
    def send(self, cmd):
        单纯的发送命令到目标服务器执行。
        :param cmd: 命令
        :type cmd: str
        self.last_line = ""
        if not cmd.endswith("\r"):
            cmd += "\r"
        self.session.send(cmd)
        self.clear_tail()
if __name__ == '__main__':
        ====示例====
    host = "127.0.0.1"  # 服务器ip
    port = 22  # ssh端口
    username = "xxx"  # 账号
    pwd = "xxx"  # 密码
    _ssh_client = SSHClient()
    _ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    _ssh_client.connect(hostname=host, timeout=60, port=port, username=username,
                        password=pwd)
    print("ssh连接成功.")
    _session = _ssh_client.get_transport().open_session(timeout=1 * 3600)  # type:Channel
    _session.get_pty()
    _session.invoke_shell()
    # ending_char为正则表达式,以"~]$ "结尾的字符作为结束字符,这里取的命令行标识符PS1的结尾
    pty_session = PtySession(_session, ending_char=r"]\$ $", timeout=10)
    pty_session.send("sleep 5\r")
    pty_session.send("touch /test_a.txt")  # 发送命令
    pty_session.exp(
            r"]\$ $",  # 期望如果以$结束
            "scp -P%s %s %s@%s:%s" % (  # 执行scp命令
                port,
                "~/test_a.txt",
                username,
                host,
                "~/test_b.txt"
    下方exp的执行伪代码类似:
    if(yes/no):
        send yes
        if (password):
            send pwd
    elif(passowrd):
        send pwd
    pty_session.exp(
            "yes/no",  # 元素1(期望结果) 如果返回的yes/no
            "yes",  # 元素2(命令) 那么发送yes
            (  # 元素3(上面的命令执行完毕后,接着这行该分支命令)
                ("password", pwd)  # 如果返回password,则输入密码。与上面注释逻辑一模一样,只是没有了第三个元素作为后续执行分支
        ("password", pwd)  # 如果直接返回的password,则输入密码
    pty_session.destroy()  # 销毁session

该篇文章代码仅供参考。

后续有空再补结合SFTP自动化文件上传下载等通用处理封装。

需求说明通过python代码来实现类似expect shell的交互式能力,这样在python代码中能结合业务逻辑组装代码,具有比expect更强大的自动化处理能力。如果能通过该交互能力,结合SFTP来实现文件相关的操作,那就更加的强大了。该篇文章代码为交互式命令封装实现。实现依赖依赖paramiko库,所以需要执行pip install paramiko来安装。代码封装实现import timeimport paramikofrom paramiko import SSHClient
Pexpect是一个纯Python类似Expect的模块 Pexpect使Python成为控制其他应用程序的更好工具。 Pexpect是用于生成子应用程序的纯Python模块。 控制他们; 并响应其输出中的预期模式。 Pexpect的作品就像Don Libes的Expect。 Pexpect允许您的脚本生成子应用程序并对其进行控制,就像人类在键入命令一样。 Pexpect可用于自动化交互式应用程序,例如ssh,ftp,passwd,telnet等。它可用于自动化安装脚本,以在不同服务器上复制软件包安装。 它可以用于自动化软件测试。 Pexpect遵循Don Libes的Expect的精神,但Pexpect是纯Python。 Pexpect的主要功能需要Python标准库中的pty模块,该模块仅在类似Unix的系统上可用。 Windows中还提供了一些功能-等待来自文件描述符或子进程的
#!/usr/bin/python import os,datetime ip = iter(["1.1","1.2","1.3","1.4","1.5","1.6","1.7","1.8"]) port = iter(["3041","4227","2300","3212","2949","4502","3767","3405"]) passwd = iter(["1...
一、pexpect模块介绍 Pexpect使Python成为控制其他应用程序的更好工具。可以理解为Linux下的expectPython封装,通过pexpect我们可以实现对ssh,ftp,passwd,telnet等命令行进行自动交互,而无需人工干涉来达到自动化的目的 二、Pexpect的安装 #python2 pip install pexpect #python3 pip3 install pexpect 三、pexpect的核心组件 3.1 spawn类 是Pexpect库的主要对象即接
Pexpect 是一个自动控制的 Python 模块,可以用来ssh、ftp、passwd、telnet 等命令行进行自动交互。 官方网站是 http://www.noah.org/。通过它,可以实现类似 expect 的操作。 例如我们可以用它来写python脚本实现批量对一系列(大量的、配置相同的)的linux服务器进行操作。 如果你对expect还不太了解,那么可以参考Li
可以使用scp命令实现非交互传输文件,例如将本地文件传输到远程服务器上: scp local_file remote_username@remote_ip:remote_folder 其中,local_file是本地文件的路径,remote_username是远程服务器的用户名,remote_ip是远程服务器的IP地址,remote_folder是远程服务器上的目标文件夹路径。