Java執行shell腳本文件完整代碼

一、背景

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章