Java魔法堂:找外援的利器——Runtime.exec详解

  1. 云栖社区>
  2. 博客>
  3. 正文

Java魔法堂:找外援的利器——Runtime.exec详解

肥仔john 2016-04-20 17:48:34 浏览1146
展开阅读全文

一、前言                                

 Java虽然五脏俱全但总有软肋,譬如获取CPU等硬件信息,当然我们可以通过JNI调用C/C++来获取,但对于对C/C++和Windows API不熟的码农是一系列复杂的学习和踩坑过程。那能不能通过简单一些、学习成本低一些的方式呢?答案是肯定的,在功能实现放在首位的情况下,借他山之石 是最简洁有力的做法。而 Runtime.exec方法 就为我们打开这么的一条路了。

 

二、认识 java.lang.Runtime.exec方法                     

  作用:用于调用外部程序,并重定向外部程序的标准输入、标准输出和标准错误到缓冲池。功能就是和windows的“运行”一样啦。

  

  方法重载:

      exec(String command) ,调用外部程序,入参command为外部可执行程序的启动路径或命令。

      exec(String[] cmdArray) ,调用外部程序,入参cmdArray的元素将组合成为一条完整的外部可执行程序的启动路径或命令。

      exec(String command, String[] envp) ,在调用外部程序之前设置系统环境变量,该变量仅供command入参使用,envp每个元素为一个系统环境变量,并且字符串格式为“环境变量名=环境变量值”。

       exec(String command, String[] envp, File dir) , 除了设置系统环境变量外,还通过入参dir设置当前工作目录。

  实例 —— 在当前目录执行dir命令,并将结果保存到c:\dir.txt文本文件中:

  前提:假设当前用户的家目录为c:\user\fsjohnhuang

  c:\user\fsjohnhuang下的目录结构

c:\user\fsjohnhuang 
|--jottings 
|--test.txt
  d:\test下的目录结构
d:\test
|--movies
|--readme.txt
  代码片段
Runtime r = Runtime.getRuntime();
try{
  Process proc = r.exec("cmd /c dir > %dest%", new String[]{"dest=c:\\dir.txt", new File("d:\\test")});
  int exitVal = proc.waitFor(); // 阻塞当前线程,并等待外部程序中止后获取结果码
  System.out.println(exitVal == 0 ? "成功" : "失败");
}
catch(Exception e){
  e.printStackTrace();
}

  执行代码后查看c:\dir.txt文件内容如如下:
驱动器 D 中的卷没有标签。
 卷的序列号是 8074-B214

 D:\test 的目录

2014/09/22  14:45    <DIR>          movies
2014/03/31  17:14             8,642 readme.txt

三、注意点                              

  1.  Runtime.exec() 不是cmd或shell环境,因此无法直接调用dir等命令。若要调用命令行下的命令,请参考第2节的实例。

  2.  通过 Process实例.getInputStream() 和 Process实例.getErrorStream() 获取的输入流和错误信息流是缓冲池向当前Java程序提供的,而不是直接获取外部程序的标准输出流和标准错误流。

  

   而缓冲池的容量是一定的,因此若外部程序在运行过程中不断向缓冲池输出内容,当缓冲池填满,那么外部程序将暂停运行直到缓冲池有空位可接收外部程序的输出内容为止。(采用xcopy命令复制大量文件时将会出现该问题)

   解决办法就是当前的Java程序不断读取缓冲池的内容,从而为腾出缓冲池的空间。

 

四、绝对是坑                              

  为解决第3节第2点注意事项中提及的问题。我们可以通过下列两种方式处理

Runtime r = Runtime.getRuntime();
try{
  Process proc = r.exec("cmd /c dir"); // 假设该操作为造成大量内容输出
  // 采用字符流读取缓冲池内容,腾出空间
  BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream(), "gbk")));
  String line = null;
  while ((line = reader.readLine()) != null){
     System.out.println(line);
  }

  // 或采用字节流读取缓冲池内容,腾出空间
  // ByteArrayOutputStream pool = new ByteArrayOutputStream();
  // byte[] buffer = new byte[1024];
  // int count = -1;
  // while ((count = proc.getInputStream().read(buffer)) != -1){
  //   pool.write(buffer, 0, count);
  //   buffer = new byte[1024];
  // }
  // System.out.println(pool.toString("gbk"));

  int exitVal = proc.waitFor();
  System.out.println(exitVal == 0 ? "成功" : "失败");
}
catch(Exception e){
  e.printStackTrace();
}

这里要注意一个坑:外部程序在执行结束后将会自动关闭,否则不管是字符流还是字节流均由于既读不到数据,又读不到流结束符而出现阻塞Java进程运行的情况。

而 cmd /c 就是告诉cmd环境进程,当执行完成后关闭自身。

 

五、总结                                  

  用适当的工具做适当的事, Runtime.exec方法 让我们功能实现的手段更灵活了!

网友评论

登录后评论
0/500
评论
肥仔john
+ 关注