需求
使用Java执行一个shell脚本或者一系列shell命令, 网上简单的教程大多存在问题: INFO流和ERR流长长是由同一个线程来操作,不能同时读取或者关闭异常,无法自动终止——导致每次Java端调完脚本后,长时间阻塞在流的读取终止中。
定位问题: 取消 INFO流和ERR流的读取后,正常返回。
改造及核心代码如下
注释在代码里
继承Thread类 重写run方法,在run方法中读取日志流
public class RunThread extends Thread {
private InputStream stream;
private String printType;
/**
* 存储info或者error信息
*/
private List<String> msgs;
public RunThread(InputStream inputStream, String printType, List<String> msgs) {
this.stream = inputStream;
this.printType = printType;
this.msgs = msgs;
}
@Override
public void run() {
try {
BufferedReader br;
try (InputStreamReader isr = new InputStreamReader(stream)) {
br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
msgs.add(printType + ">" + line);
}
}
} catch (Exception e) {
msgs.add("RunThread执行发生异常:" + e.getMessage());
}
}
}
执行shell脚本的方法
/**
* 执行shell脚本
*
* @param filePath 要运行的脚本所在的目录; 当然你也可以把要运行的脚本写成全路径。
* @return
*/
public static List<String> processShellFile(String filePath) throws Exception {
// 声明返回信息的集合 retMsgList, 使用同步安全的集合类型即可; String
List<String> retMsgList = Collections.synchronizedList(new ArrayList<>(8));
log.info("远程脚本开始执行,filePath=" + filePath);
Process process = Runtime.getRuntime().exec(filePath);
try (BufferedReader input = new BufferedReader(
new InputStreamReader(process.getInputStream(), Charset.forName("UTF-8")));
BufferedReader error = new BufferedReader(
new InputStreamReader(process.getErrorStream(), Charset.forName("UTF-8")));) {
List<String> infoList = new ArrayList<>(8);
// 开启info流读取的线程
new RunThread(process.getInputStream(), "INFO", retMsgList).start();
List<String> errList = new ArrayList<>(8);
// 开启error流读取的线程
new RunThread(process.getErrorStream(), "ERR", retMsgList).start();
int exitValue = process.waitFor();
if (exitValue == 0) {
retMsgList.add("exitValue == 0, successfully executed the envs command");
} else {
retMsgList.add("exitValue=" + exitValue + ", failure executed the envs command");
}
} catch (Exception e) {
retMsgList.add("远程脚本 Error:" + e.getMessage());
throw new Exception("远程脚本 Error,filePath=" + filePath, e);
} finally {
if (process != null) {
Thread.sleep(10);
process.destroy();
}
}
return retMsgList;
}
简单测试
/**
* 执行一条python命令 python /data/scm/scm_main_plan.py DRP 110
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
System.out.println(processShellFile("E:\PycharmProjects\python-demo-learning\day-0707-xxl\dev002.py"));
}
注意点
异常信息的格式可以自己指定, 我这里为了方便我的业务故用List
代码目前运行稳定, 起码好几个月了.
点赞~