JAVA如何调用系统命令

简介:本章主要简述了java应用程序执行的过程中,对操作系统输入命令执行,并获取操作系统执行结果。如使用调用mvn命令构建工程,其它其它的java应用程序,使用第三方服务功能等。大部分时候我们是不需要知道这个技术的,但总有意外。

实现原理:通过http请求本地地址建立链接,得到请求的输入流和输出流,通过输入流添加操作系统能执行的命令,通过输入流获取操作系统执行打印结果。(自我理解)

主要类:Process

实现方式类:Runtime与jdk1.5之后的ProcessBuilder

关系:Runtime类克隆一个与当前java虚拟机环境相同一个进程返回一个Process类

注意:每个Java应用程序都有一个Runtime类实例,使应用程序能够与其运行的环境相连接。可以通过getRuntime方法获取当前运行时环境。 应用程序不能创建自己的Runtime类实例。

Runtime API

Process exec(String command) 
          在单独的进程中执行指定的字符串命令。
Process exec(String command, String[] envp) 
          在指定环境的单独进程中执行指定的字符串命令。
Process exec(String command, String[] envp, File dir) 
          在有指定环境和工作目录的独立进程中执行指定的字符串命令。
Process exec(String[] cmdarray) 
          在单独的进程中执行指定命令和变量。 
Process exec(String[] cmdarray, String[] envp) 
          在指定环境的独立进程中执行指定命令和变量。 
Process exec(String[] cmdarray, String[] envp, File dir) 
          在指定环境和工作目录的独立进程中执行指定的命令和变量。 

command:一条指定的系统命令。

envp:环境变量字符串数组,其中每个环境变量的设置格式为name=value;如果子进程应该继承当前进程的环境,则该参数为null。

dir:子进程的工作目录;如果子进程应该继承当前进程的工作目录,则该参数为null。

cmdarray:包含所调用命令及其参数的数组。

Runtime例子

public class CommandUtil {
    /**
     * 执行操作系统命令
     * @param path  执行路径如windows的E:/test/,linux的: /usr/util/
     * @param commands
     * @return
     */
    public static List<String> command(String path, List<String> commands) {
        List<String> rspList = new ArrayList<String>();
        //获取这个进程 Runtime对象
        Runtime run = Runtime.getRuntime();
        try {
            //第一个参数是:首先执行环境命令,不同操作开启命令方式不一样
            //第二个参数是:环境变量参数(jvm的环境),这里设置为null则表示和主环境一致
            //第三个参数是:设置命令起始执行地址如windows:E:\\test\;linux:/use/util/;
            //			 相当于执行了一次cd命令
            Process proc = run.exec(getOsCmd(), null, new File(path));
            //获取正确的输出结果
            BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream(),"GB2312"));
            //获取错误的输出结果
            BufferedReader err = new BufferedReader(new InputStreamReader(proc.getErrorStream(),"GB2312"));
            //获取一个写的流,并设置true则每次执行一次则刷新缓冲区
            PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(proc.getOutputStream())), true);
            //变量执行命令(可执行cd)
            for (String line : commands) {
                out.println(line);
            }
            out.println("exit");// 这个命令必须执行,否则in流不结束。
            String rspLine = "";
            while ((rspLine = in.readLine()) != null) {
                rspList.add(rspLine);
            }
            proc.waitFor();//关闭链接
            in.close();//关闭输入流
            err.close();//关闭输入流
            out.close();//关闭输出流
            proc.destroy();//结束
        } catch (IOException e1) {
            e1.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return rspList;
    }
}
//获取当前系统环境并设置新窗口
public static String getOsCmd() {
    Properties props = System.getProperties(); //获得系统属性集
    String osName = props.getProperty("os.name"); //操作系统名称
    if (osName.toLowerCase().contains("linux")) {
        return "/bin/bash";
    } else if (osName.toLowerCase().contains("windows")) {
        return "cmd";
    } else {
        throw new BaseRuntimeException("服务器不是linux|windows操作系统");
    }
}

ProcessBuilder API

//构造方法 
 ProcessBuilder processBuilder = new ProcessBuilder();
//processBuilder.command("ping","127.0.0.1");
//利用指定的操作系统程序和参数构造一个进程生成器。 
ProcessBuilder(List<String> command) 
//利用指定的操作系统程序和参数构造一个进程生成器。
ProcessBuilder(String… command) 
//方法 
//返回此进程生成器的操作系统程序和参数。 
command() 
//设置此进程生成器的操作系统程序和参数。 
command(List<String> command) 
//设置此进程生成器的操作系统程序和参数。 
command(String… command) 
//返回此进程生成器的工作目录。 
directory() 
//设置此进程生成器的工作目录。
directory(File directory) 
//返回此进程生成器环境的字符串映射视图。 environment方法获得运行进程的环境变量,得到一个Map,可以修改环境变量 
environment() 
//返回进程生成器是否合并标准错误和标准输出;true为合并,false为不合并
redirectErrorStream() 
//设置此进程生成器的 redirectErrorStream 属性。默认值为false不合并
redirectErrorStream(boolean redirectErrorStream) 
//使用此进程生成器的属性启动一个新进程。
start()

ProcessBuilder 例子

public class CommandUtil {
    /**
     * 执行操作系统命令
     * @param path  执行路径如windows的E:/test/,linux的: /usr/util/
     * @param commands
     * @return
     */
    public static List<String> command(String path, List<String> commands) {
        List<String> rspList = new ArrayList<String>();
        try {
            String line = null;
            commands.add(0,getOsCmd())
            commands.add("exit");
            ProcessBuilder pd= new ProcessBuilder(commands);
            //设置命令起始地址如windows:E:\\test\;linux:/use/util/
            pd.directory(new File(path));
            pd.redirectErrorStream(true);//正确与错误日志一起输出
            Process p = pd.start();//启动
            //读取返回结果
            BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream(),"GB2312"));
            while((line = in.readLine()) != null){
                rspList.add(line);
			}
            p.waitFor();//关闭通道
            in.close();//关闭输入流
        } catch (IOException e1) {
            e1.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return rspList;
    }
}
//获取当前系统环境并设置新窗口
public static String getOsCmd() {
    Properties props = System.getProperties(); //获得系统属性集
    String osName = props.getProperty("os.name"); //操作系统名称
    if (osName.toLowerCase().contains("linux")) {
        return "/bin/bash";
    } else if (osName.toLowerCase().contains("windows")) {
        return "cmd";
    } else {
        throw new BaseRuntimeException("服务器不是linux|windows操作系统");
    }
}

总结

​ 在使用的过程中先知道了Runtime,在使用中也遇到过问题有

1:在操作系统执行命令需要有个起始命令。都是调起命令窗口,这就是为什么结尾都会要加一条exit命令最好。

2:Windows的是 cmd 。如果加/c则适合一条整命令的写法。可以理解为主动帮你在命令后面加了个exit命令。Linux的是/bin/bash和/bin/sh。sh本质上还是bash。如果加-c和前面理解一样

3:返回的字符是GB2312编码格式,如果在windows上中文就很容易看出乱码,所以上面才转码。

4:Windows多行命令用&&拼接,Linux多行命令用;拼接,但实际Linux用&&也行。

5:多行命令拼接为一条如果起始命令之后是cd命令则windows上有效,Linux上无效(会不会只有我┭┮﹏┭┮)

​ 最后发现Runtime与jdk1.5之后的ProcessBuilder在实现的本质上是一样的,但实现的方式可以看出ProcessBuilder的方式更加的简单方便,虽然最后我还是用的是Runtime实现(因为最先看到它),但也理解了ProcessBuilder的使用方式。