部分运维类Java应用通常需要远程连接设备并下发指令,也有一些库可以用于远程连接(例如 jsch),
但要使用这些库去实现好下发指令功能却并不容易,需要对 JavaIO有很深的理解才行。
作为开发者我们期望有一个更简单的API例如 sendCommand() 这样一次调用就能够实现执行命令获取结果,因此我开发了 commons-connect 这个项目
快速开始
首先要在项目中加入 commons-connect 依赖
<dependency>
<groupId>io.github.pxzxj</groupId>
<artifactId>commons-connect</artifactId>
<version>0.1.0</version>
</dependency>
下面是一个简单的示例展示使用 commons-connect ssh连接设备并执行两条命令得到执行结果,
其中successFlags的设置是为了判断命令执行完成
public class StartConnect {
public static void main(String[] args){
ConnectionConfigurer connectionConfigurer = ConnectionConfigurerBuilder.ssh()
.host("192.168.3.1")
.port(22)
.username("root")
.password("pwd")
.successFlags("#")
.build();
Connection connection = new SshConnectionFactory().createConnection(connectionConfigurer);
CommandConfigurer commandConfigurer = CommandConfigurerBuilder.newCommandConfigurer("df -h")
.successFlags("#")
.next("pwd")
.successFlags("#")
.build();
CommandResult commandResult = connection.sendCommand(commandConfigurer);
System.out.println("命令执行结果 = " + commandResult.getResult());
connection.close();
}
}
更多用法
详细的用法参考 项目主页,下面演示几种常见用法
1. SSH 密码登录
ConnectionConfigurer configurer = ConnectionConfigurerBuilder.ssh()
.host("192.168.3.1")
.port(22) // 22 可以省略
.username("root")
.password("password")
.successFlags("#") // 登录成功的标识
.build();
Connection connection = new SshConnectionFactory().createConnection(configurer);
2. 本地 SHELL 执行命令
有时我们不是要连接远程设备执行指令,而是直接在应用部署的设备上执行,此时可以使用 shell 类型创建连接,
在Windows中本地shell指 cmd.exe,Linux中默认使用 /bin/bash,也可以通过配置修改
ConnectionConfigurer configurer = ConnectionConfigurerBuilder.shell()
.charset("GBK") // 中文 Windows 下 cmd.exe 回显是 GBK
.successFlags(">")
.postConnect(CommandConfigurerBuilder.newCommandConfigurer("echo hello")
.enter("\r") // Windows 下必须用 \r
.successFlags(">")
.build())
.build();
Connection connection = new ShellConnectionFactory().createConnection(configurer);
换成 PowerShell 只需要改 shellPath:
ConnectionConfigurer configurer = ConnectionConfigurerBuilder.shell()
.shellPath("powershell.exe")
.charset("GBK")
.successFlags(">")
.build();
3. 下发指令与成功 / 失败标识
CommandConfigurer commandConfigurer = CommandConfigurerBuilder.newCommandConfigurer("df -h")
.successFlags("#") // 回显中出现即视为成功
.failFlags("No such file or directory") // 回显中出现即视为失败,可选
.timeoutMilliSeconds(5000) // 等待标识的超时,默认 5000
.build();
CommandResult result = connection.sendCommand(commandConfigurer);
规则很简单:
- 回显中匹配到
successFlags就立即返回,结果是成功; - 匹配到
failFlags就立即返回,结果是失败; - 两者同时出现,按失败处理;
- 两个都不配,就固定等到
timeoutMilliSeconds再返回(例如只想让命令跑一段时间,或设备提示符不固定)。
4. 分屏输出(--More--)
CommandConfigurer commandConfigurer = CommandConfigurerBuilder.newCommandConfigurer("more bigfile")
.successFlags("#")
.moreFlag("--More--") // 回显里出现这个标识说明还有下一屏
.moreCommand(" ") // 等待期间持续发送的内容,默认空格
.build();
在等待标识的过程中,一旦没有新数据可读、而 moreFlag 又配了,库就会持续发送 moreCommand 把后续分屏刷出来,所以一次调用就能拿到完整回显。
5. 多条指令链
用 next() 把命令串起来,共用一次 sendCommand():
CommandConfigurer commandConfigurer = CommandConfigurerBuilder.newCommandConfigurer("cd /opt")
.successFlags("#")
.next("ls") // 上一条成功后才执行
.successFlags("#")
.next("cat app.log")
.successFlags("#")
.build(); // build() 返回的是第一条
任意一条匹配到失败标识就终止,后面的不再执行;getFailCommand() 能告诉你是哪一条失败了,getResult() 是整条链的回显。
最后
commons-connect 解决的是我自己在项目里反复遇到的问题:别让「连设备、发指令」这件事每次都从 I/O 开始重写。如果你的项目也要连 Linux 主机、网络设备,或者只是想在本地起一个 shell 跑几条命令,可以直接试一下;一行依赖,一个 sendCommand() 就能跑起来。
欢迎反馈使用中遇到的问题和想加的特性。