部分运维类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() 就能跑起来。

欢迎反馈使用中遇到的问题和想加的特性。