我们在日常开发中,有时候会遇到触发一个linux命令,比如清理临时文件,或者触发一个shell命令。那么这个时候就需要通过java去调用shell命令。 这里我们使用java自带的ProcessBuilder 来完成任务。ProcessBuilder类是用于创建操作系统进程。通过本机远程ssh到另外一台机器上去执行shell命令的需求,这种使用方式经常用于一台机器集群管理其他机器的需求。
java里可以通过Ganymed SSH-2, Expect4j等实现ssh登录,基步骤如下:
一、下载安装
从http://www.ganymed.ethz.ch/ssh2/下载,我们用的是ganymed-ssh2-build210.zip。
在eclipse里新建一个测试工程,并将解压后的ganymed-ssh2-build210.jar拷到工程的lib目录下,然后在工程属性的java build path里添加这个jar的library。
二、ssh
将解压后的examples目录下的Basic.java 拷到工程的src目录,编译运行以后可以在控制台看到输出结果。除了执行一条命令,也可以执行一个shell脚本。
测试脚本test.sh:
#! /bin/sh
echo "testing shell"
ls
运行cmd的Java程序:
public boolean realRun(String cmd) {
cmd = test.sh
// LocalShellExecutor exe = new LocalShellExecutor();
RmtShellExecutor exe = new RmtShellExecutor("192.168.1.118", "22", "root", "123456");
// RmtShellExecutor exe = new RmtShellExecutor("127.0.0.1", "22", "root", "123456");
logger.info("cmd:" + cmd);
int ret = exe.exec(cmd);
outErr = exe.getErrorMessage();
outStr = exe.getResult();
result = (ret == 0 ? true : false);
if (!result) {
logger.error("err:" + outErr);
}
logger.info("outStr:" + outStr);
return result;
}
本地调用:
package scnas.controller;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import scnas.iface.ShellExecutor;
import scnas.model.Const;
public class LocalShellExecutor implements ShellExecutor {
/**标准输出**/
private String outStr;
/**错误输出**/
private String outErr;
@Override
public int exec(String cmds) {
int ret = -1;
try{
final Process process = Runtime.getRuntime().exec(cmds);
// Thread inputThread = new Thread(new Runnable(){
// @Override
// public void run()
// {
try {
InputStream inputStream = process.getInputStream();
InputStreamReader reader = new InputStreamReader(inputStream);
int readed = 0;
char[] chs = new char[1024];
StringBuffer buffer = new StringBuffer();
while( (readed=reader.read(chs)) != -1 ){
buffer.append(chs,0,readed);
}
outStr = buffer.toString();
logger.info(outStr);
}catch(Exception ex){
logger.error(ex.getMessage(), ex);
}
// }
// });
// Thread errorThread = new Thread(new Runnable(){
// @Override
// public void run()
// {
try {
InputStream inputStream = process.getErrorStream();
InputStreamReader reader = new InputStreamReader(inputStream);
int readed = 0;
char[] chs = new char[1024];
StringBuffer errorBuffer = new StringBuffer();
while( (readed=reader.read(chs)) != -1 )
{
errorBuffer.append(chs,0,readed);
}
outErr = errorBuffer.toString();
}catch(Exception ex){
logger.error(ex.getMessage(), ex);
}
// }
// });
// errorThread.start();
// inputThread.start();
process.getOutputStream().close();
ret = process.waitFor();
} catch(Exception e){
outErr = e.getMessage();
}
if( ret != 0 )
logger.error("LocalShellExecutor ERROR: " + outErr);
logger.info("LocalShellExecutor: " + outStr);
return ret;
}
@Override
public String getErrorMessage() {
return outErr;
}
@Override
public String getResult() {
return outStr;
}
public static void main(String args[]){
LocalShellExecutor localShellExecutor = new LocalShellExecutor();
localShellExecutor.exec(Const.PATH + Const.HOST);
}
public String getOutStr() {
return outStr;
}
public void setOutStr(String outStr) {
this.outStr = outStr;
}
public String getOutErr() {
return outErr;
}
public void setOutErr(String outErr) {
this.outErr = outErr;
}
private static final Log logger = LogFactory.getLog(LocalShellExecutor.class);
}
远程调用:
package scnas.controller;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import scnas.iface.ShellExecutor;
import scnas.model.Const;
import ch.ethz.ssh2.ChannelCondition;
import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.SCPClient;
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;
/**
* 远程执行shell脚本类
*/
public class RmtShellExecutor implements ShellExecutor{
/** */
private Connection conn;
/** 远程机器IP */
private String ip;
/** 用户名 */
private String port;
private String usr;
/** 密码 */
private String psword;
private String charset = Charset.defaultCharset().toString();
private static final int TIME_OUT = 1000 * 5 * 60;
/**标准输出**/
private String outStr;
/**错误输出**/
private String outErr;
private static final Log logger = LogFactory.getLog(RmtShellExecutor.class);
/**
* 构造函数
* @param ip
* @param usr
* @param ps
*/
public RmtShellExecutor(String ip, String port, String usr, String ps) {
this.ip = ip;
this.port = port;
this.usr = usr;
this.psword = ps;
}
/**
* 登录
*
* @return
* @throws IOException
*/
private boolean login() throws IOException {
conn = new Connection(ip, Integer.parseInt(port));
conn.connect();
return conn.authenticateWithPassword(usr, psword);
}
/**
* 执行脚本
*
* @param cmds
* @return
* @throws Exception
*/
@Override
public int exec(String cmds){
InputStream stdOut = null;
InputStream stdErr = null;
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);
ret = session.getExitStatus();
} else{
outErr = "登录远程机器失败:" + ip;
}
}catch(Exception e){
outErr = "远程操作失败:" + ip;
}finally{
if (conn != null) {
conn.close();
}
IOUtils.closeQuietly(stdOut);
IOUtils.closeQuietly(stdErr);
}
if( ret != 0 ){
logger.error("RmtShellExecutor ERROR: " + outErr);
// Loginfo loginfo = new Loginfo("ERROR", "", "RmtShellExecutor", cmds + " - " + outStr);
// scnasService.loginfoAdd(loginfo);
}else{
logger.info(cmds + ": " + outStr);
// Loginfo loginfo = new Loginfo("INFO", "", "RmtShellExecutor", cmds + " - " + outErr);
// scnasService.loginfoAdd(loginfo);
}
return ret;
}
public int exec(String cmds, boolean wait){
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);
session.waitForCondition(ChannelCondition.EXIT_STATUS, TIME_OUT );
ret = session.getExitStatus();
} else{
outErr = "登录远程机器失败:" + ip;
}
}catch(Exception e){
outErr = "远程操作失败:" + ip;
}finally{
if (conn != null) {
conn.close();
}
}
if( ret != 0 )
logger.error("RmtShellExecutor wait ERROR!");
return ret;
}
public void scp(String localfile, String remoteDirectory){
try {
if (login()) {
SCPClient sCPClient = new SCPClient(conn);
sCPClient.put(localfile, remoteDirectory, "0755");
} else{
outErr = "登录远程机器失败:" + ip;
}
} catch (IOException e) {
outErr = "远程操作失败:" + ip;
}finally{
if (conn != null) {
conn.close();
}
}
}
/**
* @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));
buf = new byte[1024];
}
return sb.toString();
}
@Override
public String getErrorMessage() {
return outErr;
}
@Override
public String getResult() {
return outStr;
}
public Connection getConn() {
return conn;
}
public void setConn(Connection conn) {
this.conn = conn;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public String getPort() {
return port;
}
public void setPort(String port) {
this.port = port;
}
public String getUsr() {
return usr;
}
public void setUsr(String usr) {
this.usr = usr;
}
public String getPsword() {
return psword;
}
public void setPsword(String psword) {
this.psword = psword;
}
public String getCharset() {
return charset;
}
public void setCharset(String charset) {
this.charset = charset;
}
public String getOutStr() {
return outStr;
}
public void setOutStr(String outStr) {
this.outStr = outStr;
}
public String getOutErr() {
return outErr;
}
public void setOutErr(String outErr) {
this.outErr = outErr;
}
public static int getTimeOut() {
return TIME_OUT;
}
public static Log getLogger() {
return logger;
}
public static void main(String args[]){
RmtShellExecutor exe = new RmtShellExecutor("10.10.1.101", "22", "root", "123456");
System.out.println(Const.SHELL + Const.PATH + Const.HOST);
// System.out.println("exit state:" + exe.exec("gluster volume status vol4 detail"));
// System.out.println("result:" + exe.getResult());
// System.out.println("err:" + exe.getErrorMessage());
// exe.scp("/tmp/1", "/tmp");
exe.exec("scnas peer status");
// System.out.println("result:" + exe.getResult());
}
}
在eclipse里运行以后的结果为:
Here is some information about the remote host:
testing shell
1
test.sh
ExitCode: 0
需要注意的是在一个session里只能执行一次命令,因此如果想在server上执行多个命令,要么打开多个session,要么在一个session里去执行一个shell脚本,shell脚本里去执行多个命令。
每次执行完以后,如果正确将返回ExitCode: 0,因此程序里可以通过sess.getExitStatus()来判断命令是否被正确执行。
三、 scp
首先在程序里import ch.ethz.ssh2.SCPClient;
然后通过下面的方法来实现:
SCPClient scpClient = conn.createSCPClient();
scpClient.put("localFiles", "remoteDirectory"); //从本地复制文件到远程目录
scpClient.get("remoteFiles","localDirectory"); //从远程获取文件
如上
public void scp(String localfile, String remoteDirectory){}
例如:
scpClient.put("D:\\localTest.txt", "/home/bill/"); 需要注意的是windows的本地目录要用双斜杠来分隔目录。
scpClient.put("/home/bill/remoteTest.txt", "D:\\");
四、sftp
首先在程序里import ch.ethz.ssh2.SFTPv3Client;
SFTPv3Client sftpClient = new SFTPv3Client(conn);
sftpClient.mkdir("newRemoteDir", 0755); //远程新建目录 ,第二个参数是创建的文件夹的读写权限
sftpClient.rmdir("oldRemoteDir"); //远程删除目录
另外还有创建删除文件,读写文件等接口,参见http://www.ganymed.ethz.ch/ssh2/javadoc/ch/ethz/ssh2/SFTPv3Client.html