我们有时候会
在java代码中,去执行一个linux shell脚本或者windows触发执行一个.bat脚本
本文章,会写一个通用的脚本工具类,通过这个工具类,可以在java代码中,调用linux shell脚本,或者window .bat脚本。
比如我们数据平台,有一个数据分析工具,当数据分析工程师,通过我们的工具,配置了某一个数据分析任务,后台的injector进程会监测到这个job,然后会调用shell脚本,去集群上执行我们的MapReduce程序。
本工具类就是实现如何java中调用linux shell脚本的, 当然windows 也是支持的。
首先定义一下结果类:
public class CommandResult {
public static final int EXIT_VALUE_TIMEOUT=-1;
int exitValue;
private String output;
private String error;
public int getExitValue() {
return exitValue;
}
public void setExitValue(int exitValue) {
this.exitValue = exitValue;
}
public String getOutput() {
return output;
}
public void setOutput(String output) {
this.output = output;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
}
接下来 ,写我们 核心的CommandUtils工具类
public class CommandUtils {
public static Logger logger = LoggerFactory.getLogger(CommandUtils.class);
public static int DEFAULT_TIMEOUT=1000;
public static final int DEFAULT_INTERVAL=100;
public static long startTime ;
public static CommandResult exec(String command){
startTime = System.nanoTime();
Process process=null;
CommandResult commandResult =null;
try {
if (null == command || "".equals(command = command.trim())) {
commandResult = new CommandResult() ;
commandResult.setExitValue(1);
commandResult.setError("command is not empty");
}
String[] cmd;
String osName = System.getProperty("os.name");
if (osName.startsWith("Windows")) {
cmd = new String[3];
if (osName.equals("Windows 95")) { // windows 95 only
cmd[0] = "command.com";
} else {
cmd[0] = "cmd.exe";
}
cmd[1] = "/C";
cmd[2] = command;
} else if (osName.equals("Linux")) { //linux
cmd = new String[3];
cmd[0] = "/bin/sh";
cmd[1] = "-c";
cmd[2] = command;
} else {
cmd = new String[1];
cmd[0] = command;
}
process= Runtime.getRuntime().exec(cmd);
commandResult = wait(process);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}finally {
if (process!=null){
process.destroy();
}
return commandResult;
}
}
//判断是否超时
public static boolean isOverTime(){
return (TimeUnit.MILLISECONDS.toNanos(DEFAULT_TIMEOUT) - (System.nanoTime() - startTime)) < 0;
}
public static CommandResult wait(Process process) throws InterruptedException, IOException {
BufferedReader errorStreamReader = null;
BufferedReader inputStreamReader = null;
try {
errorStreamReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
inputStreamReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
boolean isFinished = false;
while (true) { //这是一个死循环,直到超时,或者command 执行完成
if (isOverTime()) { //如果超时 ,直接返回
CommandResult result = new CommandResult();
result.setExitValue(CommandResult.EXIT_VALUE_TIMEOUT);
result.setOutput("Command process timeout");
return result;
}
if (isFinished) {
CommandResult result = new CommandResult();
result.setExitValue(process.waitFor());
// error info
if (errorStreamReader.ready()) {
StringBuilder buffer = new StringBuilder();
String line;
while ((line = errorStreamReader.readLine()) != null) {
buffer.append(line);
}
result.setError(buffer.toString());
}
// info
if (inputStreamReader.ready()) {
StringBuilder buffer = new StringBuilder();
String line;
while ((line = inputStreamReader.readLine()) != null) {
buffer.append(line);
}
result.setOutput(buffer.toString());
}
return result;
}
try {
isFinished = true; //很乐观的认为command 已经执行完成了 ,将isFinished 赋值为true
process.exitValue(); //这个方法,command 没有执行完成的时候会抛出异常IllegalThreadStateException被catch捕获,如果执行完成 则isFinished =true
} catch (IllegalThreadStateException e) {
// hasn't finished yet
isFinished = false; //执行到这里,说明command 没有执行完成,那么将isFinished 重新设置为false;
Thread.sleep(DEFAULT_INTERVAL); //如果command 没有执行完成,可以睡眠 DEFAULT_INTERVAL
}
}
} finally {
if (errorStreamReader != null) {
try {
errorStreamReader.close();
} catch (IOException e) {
}
}
if (inputStreamReader != null) {
try {
inputStreamReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
最后是我们的命令执行调用类:
public class InvokeCommand {
public static void main(String[] args) {
String win = "C:\\Users\\zhang\\xxx.bat";
String lin = "/user/run.sh";
int timeout = Integer.parseInt("5000");
CommandUtils.DEFAULT_TIMEOUT = timeout;
CommandResult result = CommandUtils.exec(win);
if (result != null)
{
if (result.getError()!=null) {
System.out.println("Error:" + result.getError());
}
if (result.getOutput()!=null) {
System.out.println("Output:" + result.getOutput());
}
}
}
}
上面测试了一下,win平台下的效果,结果发现.bat文件可以正常调用,并返回结果。
xxx.bat文件内容为: @echo hello boy!
下面为测试返回结果: