版权声明:本文为博主原创文章,转载请注明作者和出处! https://blog.csdn.net/tterminator/article/details/82227394
一、背景
最近要下线一台历史遗留的物理机,在整理该机的crontab任务时,发现有不少纯shell脚本形式的定时任务,决定使用Java写一个执行shell脚本文件的通用工具类。
二、实现方式
Java执行shell脚本文件共有两种方式,两种方式的共同点均是创建独立的process执行脚本文件。
方式1:通过Runtime创建process
Process process = Runtime.getRuntime().exec(cmd);
方式2:创建原生Java对象ProcessBuilder
public ProcessBuilder(String... command) {
this.command = new ArrayList<>(command.length);
for (String arg : command)
this.command.add(arg);
}
如果遇到权限问题,可以使用ProcessBuilder对象先改变脚本执行权限后,再使用ProcessBuilder对象执行该脚本,例如:
ProcessBuilder builder = new ProcessBuilder(“/bin/chmod”, “755”, tempFile.getPath());
Process process = builder.start();
int rc = process.waitFor();
虽然Java执行shell脚本时有两种方式,其实方式1本质上还是通过创建ProcessBuilder来执行shell脚本的,详见Runtime类如下exec函数代码:
public Process exec(String[] cmdarray, String[] envp, File dir)
throws IOException {
return new ProcessBuilder(cmdarray)
.environment(envp)
.directory(dir)
.start();
}
三、代码实现
Java执行shell脚本文件完整代码如下
/**
* Java执行shell脚本工具类
*/
public class ShellExcutor {
private static Logger log = LoggerFactory.getLogger("shell_logger");
/**
* Java执行shell脚本入口
* @param shellName 脚本文件名
* @throws Exception
*/
public void service(String shellName) throws Exception{
String shellDir = "";
String shellPath = "";
try {
//获取脚本所在的目录
String configFilePath = Thread.currentThread().getContextClassLoader().getResource("config.properties").getPath();
File f = new File(configFilePath);
shellDir = f.getParent();
log.info("shell dir = " + shellDir);
//拼接完整的脚本目录
shellPath = shellDir + "/shell/" + shellName;
log.info("shell path = " + shellPath);
//执行脚本
callScript(shellPath);
} catch (Exception e) {
log.error("ShellExcutor异常" + e.getMessage(), e);
throw e;
}
}
/**
* 脚本文件具体执行及脚本执行过程探测
* @param script 脚本文件绝对路径
* @throws Exception
*/
private void callScript(String script) throws Exception{
try {
String cmd = "sh " + script;
//启动独立线程等待process执行完成
CommandWaitForThread commandThread = new CommandWaitForThread(cmd);
commandThread.start();
while (!commandThread.isFinish()) {
log.info("shell " + script + " 还未执行完毕,10s后重新探测");
Thread.sleep(10000);
}
//检查脚本执行结果状态码
if(commandThread.getExitValue() != 0){
throw new Exception("shell " + script + "执行失败,exitValue = " + commandThread.getExitValue());
}
log.info("shell " + script + "执行成功,exitValue = " + commandThread.getExitValue());
}
catch (Exception e){
throw new Exception("执行脚本发生异常,脚本路径" + script, e);
}
}
/**
* 脚本函数执行线程
*/
public class CommandWaitForThread extends Thread {
private String cmd;
private boolean finish = false;
private int exitValue = -1;
public CommandWaitForThread(String cmd) {
this.cmd = cmd;
}
public void run(){
try {
//执行脚本并等待脚本执行完成
Process process = Runtime.getRuntime().exec(cmd);
//写出脚本执行中的过程信息
BufferedReader infoInput = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedReader errorInput = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String line = "";
while ((line = infoInput.readLine()) != null) {
log.info(line);
}
while ((line = errorInput.readLine()) != null) {
log.error(line);
}
infoInput.close();
errorInput.close();
//阻塞执行线程直至脚本执行完成后返回
this.exitValue = process.waitFor();
} catch (Throwable e) {
log.error("CommandWaitForThread accure exception,shell " + cmd, e);
exitValue = 110;
} finally {
finish = true;
}
}
public boolean isFinish() {
return finish;
}
public void setFinish(boolean finish) {
this.finish = finish;
}
public int getExitValue() {
return exitValue;
}
}
}
四、参考链接
https://blog.csdn.net/u010376788/article/details/51337312
https://yq.aliyun.com/articles/2362
https://blog.csdn.net/haiyang4988/article/details/75228416