此程序需要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\下的例子吧。
本文為原創,歡迎轉載,轉載請注明出處
BlogJava。