从JAVA 1.5版本开始,JAVA新增了ProcessBuilder,专门用于调用外部进程,且能快速创建一个指定了环境的进程与子进程,极大地增强了进程的复用性,示例代码如下:

//  定义在FileNameGetProcess.java文件中
static List<String> call() throws Exception {
    List<String> fileNames = Lists.newArrayList();
    //  设置了环境变量DIR,针对Windows环境
    List<String> commands = Lists.newArrayList("cmd.exe", "/c", "dir /w", "%DIR%");
    ProcessBuilder pb = new ProcessBuilder(commands);
    //  设置环境变量
    Map<String, String> env = pb.environment();
    env.put("DIR", "D:\\");
    //  启动进程
    Process pr = pb.start();
    InputStream is = pr.getInputStream(); // 获得输入流
    InputStreamReader isr = new InputStreamReader(is, "GBK");// 创建输入读流,编码方式为GBK
    BufferedReader br = new BufferedReader(isr); // 创建读缓冲对象
    while ((line = br.readLine()) != null) {
        // 循环读取数据
        fileNames.add(line);
    }
    pr.waitFor();
    return fileNames;
}

在上述代码的执行中,一定要密切注意命令参数,JAVA执行命令都通过实体文件的方式,所以执行的命令如果没有物理文件支持,如dir,则一定要采用“cmd.exe”的方式,而对于有物理文件支持的命令,如C:\Windows\System32文件下的ipconfig命令以及脚本,直接给出文件调用路径及命令即可,如:

//  直接调用
ProcessBuilder pb = new ProcessBuilder("ipconfig", "/all");
Process pr = pb.start();
//  直接调用
ProcessBuilder pb = new ProcessBuilder("E:\\list.cmd");
Process pr = pb.start();

如果违反了上述规则,则一定会出现如下错误:

Exception in thread "main" java.io.IOException: Cannot run program "dir c:\": CreateProcess error=2, 系统找不到指定的文件。
    at java.lang.ProcessBuilder.start(ProcessBuilder.java:1048)
    at com.cnitsec.mirana.process.CMDExecute.main(CMDExecute.java:106)
Caused by: java.io.IOException: CreateProcess error=2, 系统找不到指定的文件。
    at java.lang.ProcessImpl.create(Native Method)
    at java.lang.ProcessImpl.<init>(ProcessImpl.java:386)
    at java.lang.ProcessImpl.start(ProcessImpl.java:137)
    at java.lang.ProcessBuilder.start(ProcessBuilder.java:1029)
    ... 1 more

为了使进程的执行具有更好的响应性,我们可以使用Future对进程执行的任务进行封装,如下:

ExecutorService executor =  Executors.newSingleThreadExecutor();
//  获取执行结果
Future<List<String>> results = executor.submit(FileNameGetProcess::call);
List<String> strs = results.get();
for(String str: strs) {
    System.out.println(str);
}
//  一定要记得关闭,否则主线程无法结束
executor.shutdown();

结论

外部进程的执行状态可以通过Future方式来获取执行结果,ProcessBuilder.start()还支持InterruptedException,所以外部进程在执行过程中,还支持主线程的中断信号。