前言
此程序需要ganymed-ssh2-build210.jar包(下載地址:http://www.ganymed.ethz.ch/ssh2/)
為了調試方便,可以將\ganymed-ssh2-build210\src下的代碼直接拷貝到工程里,
此源碼的好處就是沒有依賴很多其他的包,拷貝過來干干凈凈.
簡介
目的:是執行遠程機器上的Shell腳本;
遠程機器IP:***.**.**.***
用戶名:sshapp
密碼:sshapp
登錄后用pwd命令,顯示當前目錄為:/sshapp.
在/sshapp/myshell/目錄下有myTest.sh文件,內容如下:
echo $1 $2 $# #print $1
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"); } }
執行后結果:
outStr=java Know 3 outErr= 0 // getExitStatus方法的返回值
注:一般情況下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, plattner@inf.ethz.ch * @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修改為如下內容:
echo $1 $2 $#
print $1由於我使用的linux機器上沒有print命令,所以print $1會報錯:command not found。
接下來再讓我們執行一下,看看控制台的結果:
outStr=java Know 3 outErr=/sshapp/myshell/myTest.sh: line 2: print: command not found 127 此時shell腳本出現錯誤,getExitStatus方法返回127.
在實際應用中,可以將outStr和outErr記錄到日志中,以便維護人員查看shell的執行情況,
而getExitStatus的返回值,可以認為是此次執行是否OK的標准。
其他代碼請看\ganymed-ssh2-build210\examples\下的例子吧。