Java执行系统命令策略

通过Java执行系统命令,与cmd中或者终端上一样执行shell命令一样。

最典型的用法:

1、Runtime.getRuntime().exec();会在给定的环境和工作目录下启动一个独立的进程运行外部命令。

2、new ProcessBuilder(cmdArray).start();

从JDK1.5开始,官方提供并推荐使用ProcessBuilder类进行shell命令操作。

首先介绍Runtime类提供了如下方法:

public long totalMemory();//返回所有可用内存空间
public long maxMemory();//返回最大可用内存空间
public long freeMemory();//返回空余内存空间
public void gc();(如果内存东西过大)手动实现JVM的gc机制。
public Process exec(String command)throws IOException//执行外部命令
Runtime.getRuntime().availableProcessors();//返回jvm可用的处理器数量

Runtime常见的几个重写的参数方法:

public Process exec(String command) throws IOException

public Process exec(String cmdarray[]) throws IOException

public Process exec(String command, String[] envp) throws IOException

public Process exec(String command, String[] envp, File dir) throws IOException

public Process exec(String[] cmdarray, String[] envp) throws IOException

public Process exec(String[] cmdarray, String[] envp, File dir) throws IOException

 对上面进行解释:

1、command:一个命令,可能包含参数和命令

2、envp:环境变量的设置;格式(name=value,name=null),null表示子进程继承当前的运行时的环境。

3、dir:子进程工作目录,null表示继承当前进程的工作目录

4、cmdarray:包含多个命令的数组,可以是命令也可以是参数

简单实现:

import java.io.BufferedReader;
import java.io.InputStreamReader;

import org.junit.Test;

public class Demo {

    @Test
    public void testName() throws Exception {
        // 调用命令
        Runtime runtime = Runtime.getRuntime();

        Process process = null;
try{
        process = runtime.exec("javac"); // 用法1:调用一个外部程序
        // process = runtime.exec("cmd /c dir"); // 用法2:调用一个指令(可包含参数)
        // process = runtime.exec("cmd /c test.bat", null, new File("D:\\test.test.bat")); // 用法3:调用一个.bat文件或者exe文件

        // 存放要输出的行数据
        String line = null;

        // 输出返回的报错信息
        System.out.println("=================ErrorStream===================");
        BufferedReader inErr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
        while ((line = inErr.readLine()) != null) {
            System.out.println(line);
        }

        // 输出正常的返回信息
        System.out.println("=================InputStream===================");
        BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
        while ((line = in.readLine()) != null) {
            System.out.println(line);
        }

        // 获取退出码
        int exitVal = process.waitFor(); // 等待进程运行结束
        //如果返回0表示执行成功,返回1表示在还行失败
        System.out.println("exitVal = " + exitVal);

        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            if (process != null) {
                process.destroy();
            }
        }
    }
}

使用Process.waitFor()Process.exitValue()的区别:

Process.waitFor()Process.exitValue()同样会返回外部程序的执行结果,但是exitValue()会阻塞直到外部程序运行结束。

指令介绍:

 cmd /c dir 是执行完dir命令后关闭命令窗口。
 cmd /k dir 是执行完dir命令后不关闭命令窗口。
 cmd /c start dir 会打开一个新窗口后执行dir指令,原窗口会关闭。
 cmd /k start dir 会打开一个新窗口后执行dir指令,原窗口不会关闭。

Runtime.exec()不是一个命令行工具:相对于系统命令行工具,Runtime.exec(..)会更加局限并且不具备跨平台功能,命令行能接收的任意字符串,Runtime.exec(..)并不一定能接收。这个陷阱通常出现在Runtime.exec(String)这个接收单个字符串执行外部命令的方法中。造成这个混乱的原因之一在于Runtime.exec(String)将传递的字符串作为外部命令的参数,而命令行中可以区分一个字符串中的多个命令。

如何解决?

就是把jechoHTT类里面输出的内容,保存到test.txt

public class CommandLineExec {
    public static void main(String[] args) throws IOException, InterruptedException {
        FileOutputStream fos = new FileOutputStream("test.txt");
        Process pid = Runtime.getRuntime().exec("java Test$jechoHTT");
        new Thread(new OutputHandlerRunnable(pid.getInputStream(), fos)).start();
        new Thread(new OutputHandlerRunnable(pid.getErrorStream())).start();
        int exitValue = pid.waitFor();
        System.out.println("Process exitValue: " + exitValue);
        fos.flush();
        fos.close();
    }

    private static class OutputHandlerRunnable implements Runnable {
        private InputStream in;
        private OutputStream os;

        public OutputHandlerRunnable(InputStream in) {
            this.in = in;
        }

        // add redirect
        public OutputHandlerRunnable(InputStream in, OutputStream redirect) {
            this(in);
            this.os = redirect;
        }

        @Override
        public void run() {
            try (BufferedReader bufr = new BufferedReader(new InputStreamReader(this.in))) {
                PrintWriter pw = null;
                if (this.os != null) {
                    pw = new PrintWriter(this.os);
                }
                String line = null;
                while ((line = bufr.readLine()) != null) {
                    System.out.println(line);
                    if (pw != null) {
                        // redirect
                        pw.println(line);
                    }
                }
                if (pw != null) {
                    pw.flush();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private static class jechoHTT {
        public static void main(String[] args) {
            System.out.println("Hello World!!!");
        }
    }
}

 我们在介绍一下ProcessBuilder这个方法执行外部命令:

优点:配置环境变量时更优雅、对当前目录的控制也更合理、错误流重定向特别方便 、进程控制更简洁。

ProcessBuilder基本方法介绍:

    构造方法摘要
    ProcessBuilder(List<String> command)
      利用指定的操作系统程序和参数构造一个进程生成器。
    ProcessBuilder(String... command)
      利用指定的操作系统程序和参数构造一个进程生成器。
 
    方法摘要
    command()
      返回此进程生成器的操作系统程序和参数。
    command(List<String> command)
      设置此进程生成器的操作系统程序和参数。
    command(String... command)
      设置此进程生成器的操作系统程序和参数。
    directory()
      返回此进程生成器的工作目录。
    directory(File directory)
      设置此进程生成器的工作目录。
    environment()
      返回此进程生成器环境的字符串映射视图。
    redirectErrorStream()
      通知进程生成器是否合并标准错误和标准输出。
    redirectErrorStream(boolean redirectErrorStream)
      设置此进程生成器的 redirectErrorStream 属性。
    start()
      使用此进程生成器的属性启动一个新进程。

基本用法:

public class ProcessTool {

    public static void main(String[] args) throws IOException {
        execWindowCmd();
    }

    public static void execWindowCmd() throws IOException {

        ProcessBuilder pb = new ProcessBuilder();// 命令
        Map<String, String> env = pb.environment();// 独立环境变量
        System.out.println(env);// 打印环境变量
        env.put("MY_NAME", "KING");// 添加环境变量key-value
        pb.redirectErrorStream(true);// 重定向错误输出流到正常输出流

        try {
            pb.directory(new File("d://test1"));// 设置目录test1
            pb.command("cmd", "/c", "dir");// 执行命令
            Process process1;
            process1 = pb.start();// 启动进程
            BufferedReader br1;
            br1 = new BufferedReader(new InputStreamReader(process1.getInputStream(), "gbk"));
            String line1 = null;
            while ((line1 = br1.readLine()) != null) {
                System.out.println(line1);
            }

            pb.directory(new File("d://test2"));// 设置目录test2
            pb.command("cmd", "/c", "dir", ">>", "test1.log");// 执行命令,把结果输出到test1.log
            Process process2 = pb.start();// 启动进程
            BufferedReader br2 = new BufferedReader(new InputStreamReader(process2.getInputStream(), "gbk"));
            String line2 = null;
            while ((line2 = br2.readLine()) != null) {//因为结果输出到了文件,所以本处无信息返回
                System.out.println(line2);
            }
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        }
    }
}

ProcessBuilder和Process的区别:

1、ProcessBuilder为进程提供了更多的控制,而Process的功能相对来说简单的多

2、ProcessBuilder是一个final类,有两个带参数的构造方法,你可以通过构造方法来直接创建ProcessBuilder的对象。而Process是一个抽象类,一般都通过Runtime.exec()和ProcessBuilder.start()来间接创建其实例。

ProcessBuilder的api:

内部类

方法描述
static class ProcessBuilder.Redirect表示子进程的输入源或者输出的目的

构造器

方法描述
ProcessBuilder​(String... command)使用操作系统程序 和参数创建一个进程生成器
ProcessBuilder​(List<String> command)使用操作系统程序和参数创建一个进程生成器

方法

取出或设置程序和参数的方法

方法描述
List<String> command()返回操作系统程序和该程序的参数。
ProcessBuilder command(List<String> command)设置这个进程生成器的 操作系统程序 和 参数。
ProcessBuilder command(String... command)置这个进程生成器的 操作系统程序 和 参数。

取出或设置工作目录的方法

方法描述
File directory()返回此进程生成器的工作目录。
ProcessBuilder directory(File directory)设置进程生成器的工作目录。

设置标准 IO 的方法

方法描述
ProcessBuilder redirectOutput(File file)使用文件作为标准输出
ProcessBuilder redirectOutput(ProcessBuilder.Redirect destination)使用 Redirect 对象作为标准输出
ProcessBuilder redirectInput(File file)重定向标准输入到文件中
ProcessBuilder redirectInput(ProcessBuilder.Redirect source)重定向标准输入
ProcessBuilder redirectError(File file)将标出错误输出设置到文件中
ProcessBuilder redirectError(ProcessBuilder.Redirect destination)设置标准错误输出的目标

取出标准 IO 的方法

方法描述
ProcessBuilder.Redirect redirectInput()返回表示标准输入的 Redirect 实例
ProcessBuilder.Redirect redirectError()返回表示标准错误输出的 Redirect 实例
ProcessBuilder.Redirect redirectOutput()返回表示标出输出的目标的 Redirect 实例

合并标准输出相关的方法

方法描述
ProcessBuilder inheritIO()子进程和父进程使用相同的标准输入,标准输出,标准错误输出
ProcessBuilder redirectErrorStream(boolean redirectErrorStream)设置 redirectErrorStream 属性
boolean redirectErrorStream()判断进程生成器是否合并了标准错误输出和标准输出。

其他方法

方法描述
Map<String,String> environment()返回保存进程生成器的环境变量的 Map 集合

启动进程方法

方法描述
Process start()使用进程生成器中设置的属性启动新一个新的进程

Process 类 API 整理

杀死子进程

方法描述
abstract void destroy()杀死子流程
Process destroyForcibly()杀死子流程

获取子进程的 IO

方法描述
abstract InputStream getErrorStream()返回连接到子进程的错误输出的输入流
abstract InputStream getInputStream()返回连接到子进程的正常输出的输入流.
abstract OutputStream getOutputStream()返回连接到子进程的正常输入的输出流

其他方法

方法描述
abstract int exitValue()返回子进程的退出值
boolean isAlive()测试子流程是否有效

等待子进程

方法描述
abstract int waitFor()使当前线程等待 Process 对象表示的进程终止
boolean waitFor(long timeout, TimeUnit unit)使当前线程在必要时等待,直到此 Process 对象代表的子进程终止或经过指定的等待时间为止。

 以上内容均来自网上参考整理。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值