• Java 学习笔记(三)调用 bat 或 shell 脚本执行


    一、背景

         在某些程序中需要 Java 代码直接 调用 bat 脚本或 sh 脚本 执行,但是除了命令不一样以外,所有的逻辑是一样的,为此以下给出通用代码。

    二、示例 + 说明

    package com.example.demo.tool;
    
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.List;
    import java.util.concurrent.atomic.AtomicBoolean;
    
    import org.apache.commons.lang3.StringUtils;
    
    import com.example.demo.test.StreamManage;
    import com.google.common.base.Joiner;
    import com.google.common.base.Splitter;
    
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j
    public class CmdUtils {
        private static AtomicBoolean error = new AtomicBoolean(false);
    
        /*
         * 启动程序
         */
        public static synchronized boolean startApp(String appParentAbsolute) {
            String osName = System.getProperty("os.name");
            String[] cmds = null;
            try {
                if (osName.toLowerCase().contains("windows")) { // windows 平台
                    cmds = new String[] { "cd /d "" + appParentAbsolute + File.separator + "script" + """,
                            "start.bat>start.log" };    // 多条命令
                    exec4Windows(cmds);
                } else if (osName.toLowerCase().contains("linux")) { // linux平台
                    cmds = new String[] { "cd "" + appParentAbsolute + File.separator + "script" + """,
                            "./start.sh>start.log" };  // 多条命令
                    exec4Linux(cmds);
                }
                return true;
            } catch (IOException e) {
                log.error("start app fail", e);
            } catch (InterruptedException e) {
                log.error("start app fail", e);
            } catch (IllegalAccessError e) {
                log.error("start app fail", e);
            }
            return false;
        }
    
        /*
         * 停止程序
         */
        public static synchronized boolean stopApp(String appParentAbsolute) {
            String osName = System.getProperty("os.name");
            String[] cmds = null;
            try {
                if (osName.toLowerCase().contains("windows")) { // windows 平台
                    cmds = new String[] { "cd /d "" + appParentAbsolute + File.separator + "script" + """,
                            "start /b stop.bat>stop.log" };
                    exec4Windows(cmds);  // 多条命令
                } else if (osName.toLowerCase().contains("linux")) { // linux平台
                    cmds = new String[] { "cd "" + appParentAbsolute + File.separator + "script" + """,
                            "./stop.sh>stop.log" };   // 多条命令
                    exec4Linux(cmds);
                }
                return true;
            } catch (IOException e) {
                log.error("stop app fail", e);
            } catch (InterruptedException e) {
                log.error("stop app fail", e);
            } catch (IllegalAccessError e) {
                log.error("stop app fail", e);
            }
            return false;
        }
    private static String exec4Windows(String[] commands) throws IOException, InterruptedException {
            String cmd = Joiner.on("&&").join(commands);
            log.info("exec cmd : {}", cmd);
            return exec("cmd /c " + cmd);  // 注意这里 将多条 windows 命令使用 && 进行拼接形成字符串,同时 在拼接字符串开头使用 cmd /c 
        }
    
        private static String exec4Linux(String[] commands) throws IOException, InterruptedException {
            String cmd = Joiner.on("&&").join(commands);
            log.info("exec cmd : {}", cmd);
            return exec(new String[]{"/bin/bash","-c",cmd});   // 注意这里 将多条 linux 命令使用 && 进行拼接形成字符串,但是这里没有在拼接的字符串开头加上 /bin/bash -c 
    经过验证直接 同windows一样拼接字符串去执行时不可行的, 只能作为一个 字符串数组 调用 Runtime.getRuntime().exec(String[])方法进行执行
    }
    private static String exec(String[] commandArray) throws IOException, InterruptedException { Process process;// Process可以控制该子进程的执行或获取该子进程的信息 log.info("exec cmd : {}", Joiner.on(" ").join(commandArray)); process = Runtime.getRuntime().exec(commandArray);// exec()方法指示Java虚拟机创建一个子进程执行指定的可执行程序,并返回与该子进程对应的Process对象实例。 return execBase(process); } private static String exec(String commands) throws IOException, InterruptedException { Process process;// Process可以控制该子进程的执行或获取该子进程的信息 log.info("exec cmd : {}", commands); process = Runtime.getRuntime().exec(commands);// exec()方法指示Java虚拟机创建一个子进程执行指定的可执行程序,并返回与该子进程对应的Process对象实例。 return execBase(process); } private static String execBase(Process process) throws IOException, InterruptedException { // 下面两个可以获取输入输出流 InputStream errorStream = process.getErrorStream(); InputStream inputStream = process.getInputStream(); String result = StringUtils.EMPTY; StreamManage stream = new StreamManage(inputStream, "normal"); StreamManage errorStreamThread = new StreamManage(errorStream, "Error"); stream.setName("normal"); stream.start(); errorStreamThread.setName("Error"); errorStreamThread.start(); int exitStatus = 0; exitStatus = process.waitFor();// 等待子进程完成再往下执行,返回值是子线程执行完毕的返回值 // 第二种接受返回值的方法 int i = process.exitValue(); // 接收执行完毕的返回值 log.debug("i----" + i); if (exitStatus != 0) { log.error("exec cmd exitStatus {}", exitStatus); } else { log.debug("exec cmd exitStatus {}", exitStatus); } errorStreamThread.setEnd(true); stream.setEnd(true); process.destroy(); // 销毁子进程 process = null; if (exitStatus != 0 || StringUtils.isNotEmpty(errorStreamThread.getResult())) { result = errorStreamThread.getResult(); throw new IllegalAccessError("exe fail,message:" + result); } else if (StringUtils.isNotEmpty(stream.getResult())) { result = stream.getResult(); } return result; } }

     接收返回的数据流

    package com.example.demo.test;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.nio.charset.Charset;
    
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j
    public class StreamManage extends Thread{
        private InputStream inputStream;
        private String type;
        private String result;
        boolean end = false;
    
        //StreamManage的构造参数是InputStream类实例 => 实际上是Process实例的返回流
        public StreamManage(InputStream inputStream,String type){
            this.inputStream = inputStream;
            this.type = type;
            this.end = false;
        }
        public void run(){
            log.info("start thread to read cmd ");
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream,Charset.forName("GBK"));
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String line = null;
            StringBuffer resultBuffer = new StringBuffer();
            try{
                while(!end){
                    if((line = bufferedReader.readLine())!=null){
                        if (type.equals("Error")) {
                            log.error(line);
                        }else{
                            log.debug(line);
                        }
                        resultBuffer.append(line);
                        resultBuffer.append("
    ");
                    }
                }
                log.info("result:[{}]", resultBuffer.toString());
                result = resultBuffer.toString();
            }catch (IOException e){
                //ignore
            }catch(Throwable e){
                log.warn("cmd fail.",e);
            }finally{
                end = true;
            }
        }
        public String getResult() {
            return result;
        }
        public boolean isEnd() {
            return end;
        }
        public void setEnd(boolean end) {
            this.end = end;
        }    
    }

    这里需要强调的是:

      通过 Runtime.getRuntime().exec() 直接执行 Linux 命令 或调用 Linux 脚本的时候,只能使用 通过方法 Runtime.getRuntime().exec(new String[]{"/bin/bash","-c",commond}) 方法,否则执行不了。

  • 相关阅读:
    mysql 递归查找菜单节点的所有子节点
    mapStruct笔记
    JavaBean映射工具dozer学习
    常见Bean映射工具分析评测及Orika介绍
    Java 实体-实体的映射框架
    实体类与实体DTO类之间的转换
    推荐一个 Java 实体映射工具 MapStruct
    java Web项目Service层通用接口和entityVo对象与entity对象转化问题的解决方案
    SpringData JPA进阶查询—JPQL/原生SQL查询、分页处理、部分字段映射查询
    JPA框架下使用纯粹的原生SQL
  • 原文地址:https://www.cnblogs.com/sandyflower/p/13684894.html
Copyright © 2020-2023  润新知