Java可以通过Runtime.getRuntime().exec()方法调用linux平台下的命令及Shell脚本。
考虑到阻塞问题以及为了获取命令输出,文中使用了exitValue方法。
代码如下
ShellUtils:执行外部命令的工具类
package com.wll.shell;
import com.wll.utils.CommonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class ShellUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(ShellUtils.class);
private static final long THREAD_SLEEP_TIME = 10;
private static final int DEFAULT_WAIT_TIME = 20 * 60 * 1000;
public static void runShell(String cmd) {
String[] command = new String[]{"/bin/sh", "-c", cmd};
try {
Process process = Runtime.getRuntime().exec(command);
ShellResult result = getProcessResult(process, DEFAULT_WAIT_TIME);
LOGGER.info("Command [{}] executed successfully.", cmd);
LOGGER.info(result.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取命令执行结果
* @param process 子进程
* @param waitTime 指定超时时间
* @return 命令执行输出结果
*/
public static ShellResult getProcessResult(Process process, long waitTime) {
ShellResult cmdResult = new ShellResult();
boolean isTimeout = false;
long loopNumber = waitTime / THREAD_SLEEP_TIME;
long realLoopNumber = 0;
int exitValue = -1;
StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream());
StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream());
errorGobbler.start();
outputGobbler.start();
try {
while (true) {
try {
Thread.sleep(THREAD_SLEEP_TIME);
exitValue = process.exitValue();
break;
} catch (InterruptedException e) {
realLoopNumber++;
if (realLoopNumber >= loopNumber) {
isTimeout = true;
break;
}
}
}
errorGobbler.join();
outputGobbler.join();
if (isTimeout) {
cmdResult.setErrorCode(ShellResult.TIMEOUT);
return cmdResult;
}
cmdResult.setErrorCode(exitValue);
if (exitValue != ShellResult.SUCCESS) {
cmdResult.setDescription(errorGobbler.getOutput());
} else {
cmdResult.setDescription(outputGobbler.getOutput());
}
} catch (InterruptedException e) {
LOGGER.error("Get shell result error.");
cmdResult.setErrorCode(ShellResult.ERROR);
} finally {
CommonUtils.closeStream(process.getErrorStream());
CommonUtils.closeStream(process.getInputStream());
CommonUtils.closeStream(process.getOutputStream());
}
return cmdResult;
}
}
StreamGobbler:读取命令输出流和错误流的工具类
package com.wll.shell;
import com.wll.utils.CommonUtils;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class StreamGobbler extends Thread {
private InputStream is;
private List output = new ArrayList();
public StreamGobbler(InputStream is) {
this.is = is;
}
public List getOutput() {
return output;
}
@Override
public void run() {
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
String line = "";
while ((line = reader.readLine()) != null) {
output.add(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
CommonUtils.closeStream(reader);
}
}
}
ShellResult:命令执行结果
package com.wll.shell;
import java.util.List;
public class ShellResult {
public static final int SUCCESS = 0;
public static final int ERROR = 1;
public static final int TIMEOUT = 13;
private int errorCode;
private List description;
public int getErrorCode() {
return errorCode;
}
public void setErrorCode(int errorCode) {
this.errorCode = errorCode;
}
public List getDescription() {
return description;
}
public void setDescription(List description) {
this.description = description;
}
@Override
public String toString() {
return "ShellResult{" +
"errorCode=" + errorCode +
", description=" + description +
'}';
}
}
ShellTest:测试类
package com.wll.shell;
public class ShellTest {
public static void main(String[] args) {
String cmd = "";
if (args.length == 1) {
cmd = args[0];
}
ShellUtils.runShell(cmd);
}
}
另外,由于流关闭操作用得比较频繁,故单独写了个工具类。
package com.wll.utils;
import org.apache.log4j.Logger;
import java.io.Closeable;
import java.io.IOException;
public class CommonUtils {
private static final Logger LOGGER = Logger.getLogger(CommonUtils.class);
/**
* 提供统一关闭流的方法
*
* @param stream 待关闭的流
*/
public static void closeStream(Closeable stream) {
if (stream == null) {
return;
}
try {
stream.close();
} catch (IOException e) {
LOGGER.error("Close stream failed!");
}
}
}
代码放在CentOS下,详细目录结构如下:
以下为测试脚本,十分简单,只是输出当前日期和时间
最终运行结果如下: