java使用ganymed ssh-2执行linux远程操作命令

203 阅读2分钟

目前市面上执行远程操作可选方案并不多,一般用到两种:jsch 和 ganymed ssh-2

第一种:使用ganymed ssh-2
优点:是基于原生java的ssh操作开源库,特点是api语义清晰使用方便,license友好
缺点:2014年就已经停止更新维护了

mvn依赖配置:

<dependency>
    <groupId>ch.ethz.ganymed</groupId>
    <artifactId>ganymed-ssh2</artifactId>
    <version>262</version>
</dependency>

连接方式支持账号密码登陆和秘钥登陆

Connection conn = new Connection("ip地址");
conn.connect(null, Constant.CONNECT_OUT_TIME, Constant.KEX_OUT_TIME);
//账号密码登陆
conn.authenticateWithPassword(userName, password);
//秘钥登陆
conn.authenticateWithPublicKey(userName, new File("秘钥绝对路径"), password);

执行远程命令

session1 = connection.openSession();
//执行rsync同步
String cmd = String.format("rsync --protect-args -avlPz "%s"  "%s@%s:%s" ", sourcePath,
        targetUsername, targetIp, targetParentPath.replaceAll("\s", "\ "));
//输出命令执行日志
String stdouts = " >>/var/log/vss/" + executeLog.getId() + ".log";
String command = String.format("sshpass -p %s  ", targetPassword) + cmd + stdouts;
session.execCommand(command);

// pid   可以新会话session2 查询第一个会话命令在linux的执行进程id   保存到
session2 = connection.openSession();
String vssFlag = targetPath.substring(targetPath.lastIndexOf("/"));
session2.execCommand("ps -ef|grep rsync|grep " + vssFlag);
br = new BufferedReader(new InputStreamReader(pssession.getStdout()));
String line;
while ((line = br.readLine()) != null) {

    if (line.endsWith(stdouts)) {
        String[] temsp = line.split("\s+");
         cmd_id = Integer.parseInt(temsp[1]);
         //cmd_id 执行命令进程号 可以保存到数据库;当执行退出后,可以再次连接到服务器,
         然后查询进程是否真正退出结束了
    }
}

// pid end   等待远程执行命令的退出码   超时时间为0 表示无限期等待下去  当命令执行时间过长时
//存在退出码已经返回,执行命令的进程还未退出的情况

int condition = session.waitForCondition(ChannelCondition.EXIT_STATUS, 0);

if ((condition & ChannelCondition.EXIT_STATUS) != 0) {
    Integer exitCode = session.getExitStatus();
    log.info("EXIT_STATUS {} ", exitCode);

    if (exitCode != null && exitCode != 0) {
        errorReader = new BufferedReader(new InputStreamReader(session.getStderr()));

        StringBuilder sb = new StringBuilder();

        line = null;
        while ((line = errorReader.readLine()) != null) {
            log.error(line);
            sb.append(line);
        }

        throw new IOException(sb.toString());
    }

}

常见问题

  1. Session对象只能执行一次execCommand操作,调用多次会报错。如果想要调用多条命令,彼此不依赖可以创建多个Session对象,也可以用";"分割多条操作一次传入;

  2. session的waitForCondition(int channelCondition, long timeout)方法可以等待期待的状态来临。遇到过流读完了但ExitCode还是Null的情况,所以保险期间需要等待获得远程进程的退出状态。ChannelCondition里的常量定义了相关的状态码,同一时间状态可以有多个,输入用或操作|连接多个状态码, 可以表达多个状态任意一个达成即可的语义。除了指定的状态来临,当超时或session关闭时,方法也会立刻返回。返回值也是int channelCondition,可以跟ChannelCondition的常量与操作 &,判断是否有期待的状态。