乐趣区

关于java:Java魔法堂调用外部程序

前言

Java 尽管五脏俱全但总有软肋,譬如获取 CPU 等硬件信息,当然咱们能够通过 JNI 调用 C /C++ 来获取,但对于对 C /C++ 和 Windows API 不熟的码农是一系列简单的学习和踩坑过程。那能不能通过简略一些、学习成本低一些的形式呢?答案是必定的,在性能实现放在首位的状况下,借他山之石是最简洁无力的做法。

意识 java.lang.Runtime#exec 办法

作用:用于调用内部程序,并重定向内部程序的规范输出、规范输入和规范谬误到缓冲池。性能就是和 windows 的“运行”一样。

重载办法阐明

Runtime#exec(String command);
Runtime#exec(String command, String[] envp);
Runtime#exec(String command, String[] envp, File workdir);
Runtime#exec(String[] cmdArray);
Runtime#exec(String[] cmdArray, String[] envp);
Runtime#exec(String[] cmdArray, String[] envp, File workdir);
  1. String[] envp 作为调用命令前设置的会话级环境变量。

1.1. 变量作用域:命令运行完结后,通过该参数设置的环境变量将生效;
1.2. 设置形式:variableName=variableValue,如Process proc = r.exec("cmd /c dir > %dest%", new String[]{"dest=c:\\dir.txt"});

  1. File workdir 用于设置当前工作目录,譬如咱们须要执行位于 D:\tools 下的 echo.exe 程序,那么能够这样调用Process proc = r.exec("echo.exec", null, new File("D:\\tools"));
  2. String command 即为须要调用的内部程序,以及命令行参数等。Windows 下调用系统命令,像 dir 等命令是由 cmd 解析器解释执行的,因而若间接写 ”dir” 则会被认为在当前工作目录下有一个 ”dir.exe” 文件,那么当然会执行失败;在 Linux 下调用 ls 等是同样情理,因而请按如下形式调用 cmd 和 shell 命令:

3.1. 调用 CMD 命令的形式为 Process proc = r.exec(String.format("cmd /c %s", "cmd 命令,如 dir、type 等")),若要启动一个新的 Console 执行命令,只须要将dir 改写为 start dir 即可;
3.2. 调用 Shell 命令的形式为 Process proc = r.exec(String.format("/bin/sh -c %s", "shell 命令,如 ls、cat 等")),若要启动一个新的 Terminal 执行命令,只须要将ls 改写为 xterm -e ls 即可;

  1. String[] cmdArray 性能和 String command 一样,但命令行的每个局部将作被独立分隔进去作为数组中的元素。如 cmd /c dir 必须宰割为new String[]{"cmd", "/c", "dir"},而不能宰割为new String[]{"cmd /c", "dir"}

输出重定向

废话少说,间接看代码!

try {                                                                                                           
  String cmd = "cmd /c start cmd.exe";                                                                        
  Process child = Runtime.getRuntime().exec(cmd);                                                             
  OutputStream output = child.getOutputStream();                                                              
  output.write("cd C:/ /r/n".getBytes());                                                                     
  output.flush();                                                                                             
  output.write("dir /r/n".getBytes());                                                                        
  output.close();}                                                                                                               
catch (IOException e) {}   

输入重定向

留神:不反对间接应用 > 或 >> 执行规范输入重定向。

String cmd = "/path/to/getipconfig.bat"; // 本人写的 bat 脚本文件,外面蕴含 ipconfig /all 命令。Process p;                                                                                                      
                                                                                                                      
try {p = Runtime.getRunTime().exec(cmd);                                                                         
  InputStream input = p.getInputStream();                                                                     
  InputStreamReader streamReader = new InputStreamReader(input);                                              
  BufferedReader bufReader = new BufferedReader(streamReader);                                                
  String line = null;                                                                                         
                                                                                                                     
  while ((line = bufReader.readLine()) != null) {System.out.println(line);                                                                                   
  }                                                                                                           
}                                                                                                               
catch (IOException e) {}                

为什么不能应用 >>>实现输入重定向呢?

通过 Process 实例.getInputStream()Process 实例.getErrorStream()获取的输出流和谬误信息流是缓冲池是以后 Java 程序提供的,而不是间接获取内部程序的规范输入流和规范谬误流。
即通过 Runtime#exec 调用内部程序时,内部程序的规范输入流和规范谬误流曾经被 Java 程序接管。那么在命令中希图通过 >>>实现输入重定向显然已无成果。

另外,缓冲池的容量是肯定的,因而若内部程序在运行过程中一直向缓冲池输入内容,当缓冲池填满,那么内部程序将暂停运行直到缓冲池有空位可接管内部程序的输入内容为止。(采纳 xcopy 命令复制大量文件时将会呈现该问题)
解决办法就是以后的 Java 程序一直读取缓冲池的内容,从而为腾出缓冲池的空间。如:

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 过程运行的状况。

简化输入输出重定向的java.lang.ProcessBuilder

性能和 java.lang.runtime#exec 一样,只是java.lang.ProcessBuilder 仅接管命令行以数组模式传递给 java.lang.ProcessBuilder#command() 而已。

根本应用

ProcessBuilder pb = new ProcessBuilder();                                                                                                                                  
// pb.command("cmd /c dir"); 这样会报错                                                                                                                                    
// 或者写到 new ProcessBuilder("cmd", "/c", "dir")                                                                                                                          
pb.command("cmd", "/c", "dir");                                                                                                                                            
try {Process process = pb.start();                                                                                                                                          
  // 前面都是操作 Process 实例的办法                                                                                                                                       
}                                                                                                                                                                          
catch (IOException e){}                                                                                                                                                    

重定向

public ProcessBuilder redirectInput(File file)  // 输出重定向                                                                                                              
public ProcessBuilder redirectOutput(File file) // 输入重定向                                                                                                             
public ProcessBuilder redirectError(File file)  // 异样重定向                                                                                                              

示例

try {ProcessBuilder pb = new ProcessBuilder("cmd");                                                                                                                       
  // 在规范输出中,通过换行符分隔多行命令。// commands.txt 的内容为                                                                                                                                              
  // javac Demo.java                                                                                                                                                   
  // java Demo                                                                                                                                                         
  File commands = new File("/path/to/commands.txt");                                                                                                                   
  File error = new File("/path/to/error");                                                                                                                             
  File output = new File("/path/to/output");                                                                                                                           
                                                                                                                                                                                 
  pd.redirectInput(commands);                                                                                                                                          
  pd.redirectOutput(output);                                                                                                                                           
  pd.redirectError(error);                                                                                                                                             
                                                                                                                                                                                 
  pd.start();}                                                                                                                                                                        
catch(Exception e) {e.printStackTrace();                                                                                                                                                 
}                                                                                                                                                                        

java.lang.ProcessAPI 阐明

// 以非阻塞形式获取子过程执行的返回值(习惯 0 示意失常完结)。若子过程尚未实现时调用该办法,则会报异样 `java.lang.IllegalThreadStateException`
int exitValue()            
// 以阻塞形式获取子过程执行的返回值。若过程尚未实现则会期待子过程实现后才复原以后线程。// 问题:若子过程无奈失常敞开,则会导致 Java 线程始终挂起;// 返回值为子过程的退出码
int waitFor()。// 如果超时前子过程完结,那么返回 `true`,否则返回 `false`
boolean waitFor(long timeout, TimeUnit unit)
// 强行终止子过程,但调用后子过程不会马上被终止,所以立刻调 `boolean isAlive()` 办法可能会返回 `true`,因而需配合 `waitFor` 应用。void destory()
// 默认实现为调用 `void destory()` 办法。从 JDK1.8 开始提供。Process destoryForcibly()
// 如果子过程还没完结则返回 `true`。从 JDK1.8 开始提供。boolean isAlive()
// 获取子过程的异样输入流,如果子过程以 `ProcessBuilder` 创立,且通过 `ProcessBuilder.redirectError` 设置重定向,那么该办法返回 `null`                                                   
InputStream getErrorStream()
// 获取子过程的规范输入流,如果子过程以 `ProcessBuilder` 创立,且通过 `ProcessBuilder.redirectOutput` 设置重定向,那么该办法返回 `null`
InputStream getInputStream()  
// 获取子过程的规范输出流,如果子过程以 `ProcessBuilder` 创立,且通过 `ProcessBuilder.redirectInput` 设置重定向,那么该办法返回 `null`
OutputStream getOutputStream()

总结

尊重原创,转载请注明来自:https://www.cnblogs.com/fsjoh… ^_^ 肥仔 John

退出移动版