此程序需要ganymed-ssh2-build210.jar包。
下载地址:http://www.ganymed.ethz.ch/ssh2/
为了调试方便,可以将\ganymed-ssh2-build210\src下的代码直接拷贝到我们的工程里,
此源码的好处就是没有依赖很多其他的包,拷贝过来干干净净。
此程序的目的是执行远程机器上的Shell脚本。
远程机器IP:***.**.**.***
用户名:sshapp
密码:sshapp
登录后用pwd命令,显示当前目录为:/sshapp.
在/sshapp/myshell/目录下有myTest.sh文件,内容如下:
我们的Java代码RmtShellExecutor.java:
- /** *//**
- * 远程执行shell脚本类
- * @author l
- */
- public class RmtShellExecutor {
- /** *//** */
- private Connection conn;
- /** *//** 远程机器IP */
- private String ip;
- /** *//** 用户名 */
- private String usr;
- /** *//** 密码 */
- private String psword;
- private String charset = Charset.defaultCharset().toString();
- private static final int TIME_OUT = 1000 * 5 * 60;
- /** *//**
- * 构造函数
- * @param param 传入参数Bean 一些属性的getter setter 实现略
- */
- public RmtShellExecutor(ShellParam param) {
- this.ip = param.getIp();
- this.usr = param.getUsername();
- this.psword = param.getPassword();
- }
- /** *//**
- * 构造函数
- * @param ip
- * @param usr
- * @param ps
- */
- public RmtShellExecutor(String ip, String usr, String ps) {
- this.ip = ip;
- this.usr = usr;
- this.psword = ps;
- }
- /** *//**
- * 登录
- *
- * @return
- * @throws IOException
- */
- private boolean login() throws IOException {
- conn = new Connection(ip);
- conn.connect();
- return conn.authenticateWithPassword(usr, psword);
- }
- /** *//**
- * 执行脚本
- *
- * @param cmds
- * @return
- * @throws Exception
- */
- public int exec(String cmds) throws Exception {
- InputStream stdOut = null;
- InputStream stdErr = null;
- String outStr = "";
- String outErr = "";
- int ret = -1;
- try {
- if (login()) {
- // Open a new {@link Session} on this connection
- Session session = conn.openSession();
- // Execute a command on the remote machine.
- session.execCommand(cmds);
- stdOut = new StreamGobbler(session.getStdout());
- outStr = processStream(stdOut, charset);
- stdErr = new StreamGobbler(session.getStderr());
- outErr = processStream(stdErr, charset);
- session.waitForCondition(ChannelCondition.EXIT_STATUS, TIME_OUT);
- System.out.println("outStr=" + outStr);
- System.out.println("outErr=" + outErr);
- ret = session.getExitStatus();
- } else {
- throw new AppException("登录远程机器失败" + ip); // 自定义异常类 实现略
- }
- } finally {
- if (conn != null) {
- conn.close();
- }
- IOUtils.closeQuietly(stdOut);
- IOUtils.closeQuietly(stdErr);
- }
- return ret;
- }
- /** *//**
- * @param in
- * @param charset
- * @return
- * @throws IOException
- * @throws UnsupportedEncodingException
- */
- private String processStream(InputStream in, String charset) throws Exception {
- byte[] buf = new byte[1024];
- StringBuilder sb = new StringBuilder();
- while (in.read(buf) != -1) {
- sb.append(new String(buf, charset));
- }
- return sb.toString();
- }
- public static void main(String args[]) throws Exception {
- RmtShellExecutor exe = new RmtShellExecutor("***.**.**.***", "sshapp", "sshapp");
- // 执行myTest.sh 参数为java Know dummy
- System.out.println(exe.exec("sh /webapp/myshell/myTest.sh java Know dummy"));
- // exe.exec("uname -a && date && uptime && who");
- }
- }
执行后结果:
注:一般情况下shell脚本正常执行完毕,getExitStatus方法返回0。
此方法通过远程命令取得Exit Code/status。但并不是每个server设计时都会返回这个值,如果没有则会返回null。
在调用getExitStatus时,要先调用WaitForCondition方法,通过ChannelCondition.java接口的定义可以看到每个条件的具体含义。见以下代码:
ChannelCondition.java
- package ch.ethz.ssh2;
- /** *//**
- * Contains constants that can be used to specify what conditions to wait for on
- * a SSH-2 channel (e.g., represented by a {@link Session}).
- *
- * @see Session#waitForCondition(int, long)
- *
- * @author Christian Plattner, [email protected]
- * @version $Id: ChannelCondition.java,v 1.6 2006/08/11 12:24:00 cplattne Exp $
- */
- public abstract interface ChannelCondition
- {
- /** *//**
- * A timeout has occurred, none of your requested conditions is fulfilled.
- * However, other conditions may be true - therefore, NEVER use the "=="
- * operator to test for this (or any other) condition. Always use
- * something like <code>((cond & ChannelCondition.CLOSED) != 0)</code>.
- */
- public static final int TIMEOUT = 1;
- /** *//**
- * The underlying SSH-2 channel, however not necessarily the whole connection,
- * has been closed. This implies <code>EOF</code>. Note that there may still
- * be unread stdout or stderr data in the local window, i.e, <code>STDOUT_DATA</code>
- * or/and <code>STDERR_DATA</code> may be set at the same time.
- */
- public static final int CLOSED = 2;
- /** *//**
- * There is stdout data available that is ready to be consumed.
- */
- public static final int STDOUT_DATA = 4;
- /** *//**
- * There is stderr data available that is ready to be consumed.
- */
- public static final int STDERR_DATA = 8;
- /** *//**
- * EOF on has been reached, no more _new_ stdout or stderr data will arrive
- * from the remote server. However, there may be unread stdout or stderr
- * data, i.e, <code>STDOUT_DATA</code> or/and <code>STDERR_DATA</code>
- * may be set at the same time.
- */
- public static final int EOF = 16;
- /** *//**
- * The exit status of the remote process is available.
- * Some servers never send the exist status, or occasionally "forget" to do so.
- */
- public static final int EXIT_STATUS = 32;
- /** *//**
- * The exit signal of the remote process is available.
- */
- public static final int EXIT_SIGNAL = 64;
- }
当我们把myTest.sh修改为如下内容:
由于我使用的linux机器上没有print命令,所以print $1会报错:command not found。
接下来再让我们执行一下,看看控制台的结果:
此时shell脚本出现错误,getExitStatus方法返回127.
在实际应用中,可以将outStr和outErr记录到日志中,以便维护人员查看shell的执行情况,
而getExitStatus的返回值,可以认为是此次执行是否OK的标准。
其他代码请看\ganymed-ssh2-build210\examples\下的例子吧。