使用expect4j完成一个交互式处理的java工具
最近要管理很多网络设备由于配置都差不多配什么静态路由啊,黑名单,等等。本来利用python的ansible可以对linux服务器进行这样的操作的但是这种网络设备完全不适用,他们无法执行基本的bash命令。故而只能使用基于命令行操作的方式来处理。程序的作用就是降低重复大量的机械劳动。
介绍下expect的基本原理通过ssh链接读取字节流输出字节流,然后进行匹配看是否完成常用的是正则匹配、超时匹配、EOF匹配等,目前我用的主要是正则和超时,匹配过后可以针对读取的字节流进行操作,将他们保存在一个buffer当中。
接下来开始结构说明
//实例化一个ssh链接
session = new JSch().getSession(USERNAME, HOST_IP, HOST_PORT);
//输入密码
session.setPassword(passwd);
//链接的配置
session.setConfig("StrictHostKeyChecking", "no");
//建立ssh链接
session.connect();
//打开一个终端
channel = (ChannelShell) session.openChannel("shell");
//建立一个expect的输入输出流实例
expect = new Expect4j(channel.getInputStream(), channel.getOutputStream());
//设置expect的默认超时时间(毫秒)
expect.setDefaultTimeout(2000);
//终端链接 设定一个超时时间
channel.connect(sshTimeout + 500);
这样我们就使用ssh连接到设备的终端上进而可以开始输入命令每个命令后面必带一个回车
public static final String BACKSLASH_R = "\r";//回车
expect.send(String.format("ssh %s %s", ip, HOST_PORT));
expect.send(BACKSLASH_R);
这样我们就能把命令输出出去了,但是如果我们想要看结果就需要使用expect.expect方法
这里使用的是<作为结束如果输出流遇到<就会将他分割,后面是lambda对他的操作每次操作过后expect里面的内置buffer会从匹配的地方截断只有后面的,这样buffer里面就存入了输出数据了
StringBuffer buffer = new StringBuffer();
//判断是否结束
expect.expect("<", state -> buffer.append(state.getBuffer()));
对于经常要分页输出空格的比如linux的more命令之后要不停的按空格之类的我们可以采用这样的方法
这里解释下COMMAND_EXECUTION_TIMEOUT 这个-2的意思,其实expect是一个多态里面最终运行的是一个Match接口的列表
当我们匹配到列表顺序的Match之后就会返回他的值,这里我只有一个所以程序匹配超时时间的时候是-2 如果你是自己写了一个list那么要注意这里的顺序
private static final int COMMAND_EXECUTION_TIMEOUT = -2;
public static final String MORE_N = " ";//输出空格
while (expect.expect("---- More ----", x -> buffer.append(x.getBuffer())) != COMMAND_EXECUTION_TIMEOUT) {
expect.send(MORE_N);
}
这样“----- More ------”就会被跳过,整个输出也会出现
最后只要再加上之前判断是否结束的标志就能完成整个输出了
最后有些ping命令之类耗时较长的会因为默认10秒超时导致没有输出这里我们可以自己定义一个超时时间
expect.setDefaultTimeout(timeout);
贡献2个网络设备常用的方法方便大家使用
private static final int COMMAND_EXECUTION_TIMEOUT = -2;
private static final int COMMAND_EXECUTION_SUCCESS_OPCODE = 0;
public static final String BACKSLASH_R = "\r";
public static final String MORE_N = " ";
public String executeCommand(String command) throws Exception {
StringBuffer buffer = new StringBuffer();
//清空之前发送的信息,并发送命令
if (expect.expect(">", state -> buffer.setLength(0)) != COMMAND_EXECUTION_TIMEOUT) {
expect.send(command);
expect.send(BACKSLASH_R);
}
// 处理more需要空格才能显示完毕的问题
while (expect.expect("---- More ----", x -> buffer.append(x.getBuffer())) != COMMAND_EXECUTION_TIMEOUT) {
expect.send(MORE_N);
}
//判断是否结束
expect.expect("<", state -> buffer.append(state.getBuffer()));
//去掉命令行 和最后一个<
return buffer.length() > 0 ? buffer.deleteCharAt(buffer.length() - 1).substring(command.length()) : "没有输出";
}
public void executeCommands(List<String> commands) throws Exception {
expect.getLastState().setBuffer(null);
//清空之前发送的信息,并发送命令
expect.expect(">");
expect.send("sys");
expect.send(BACKSLASH_R);
if (expect.expect("]") != COMMAND_EXECUTION_TIMEOUT) {
for (String command : commands) {
expect.send(command);
expect.send(BACKSLASH_R);
}
}
expect.expect("]");
String buffer = expect.getLastState().getBuffer();
if (buffer.length() > 0)
System.out.println(buffer.substring(0, buffer.length() - 1));
}