一、背景
最近要下線一臺歷史遺留的物理機,在整理該機的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