在Java程序中可能须要调用底层操做系统上的其余程序,Java标准API提供了建立底层操做系统上运行的进程的能力,只须要传入正确的命令和相关的参数,就能够启动一个进程。在进程启动以后,能够从Java程序向进程提供输入数据,以及读取进程运行过程当中产生的输出数据。对于在Java程序中启动其余进程这个任务来讲,最重要的是输入和输出的处理。一般的作法是把Java程序的内部运行结果做为输入传递给一个新建立的进程,而后等待进程执行完成。在获得进程输出的运行结果以后,再继续下面的处理。经过这种方式,底层操做系统上的其余进程能够很好地与Java程序集成起来。java
在Java7以前,对进程的输入和输出进行处理的方式比较有限,只支持管道式的方式。进程的输入对Java程序来讲是一个输出流,程序向这个输出流中写入的数据会经过管道传递给进程。一样的,进程的输出对于java程序来讲是一个输入流,经过读取此输入流的内容得到进程的输出。标准的建立新进程的过程是使用:java.lang.ProcessBuilder类来设置新进程的属性,而后经过start方法来启动进程的执行。ProcessBuilder类的start方法的返回值是一个表示进程的java.lang.Process类的对象。经过Process类的getOutputStream方法能够获得向进程写入数据的输出流,而经过getInputStream和getErrorStream方法能够分别获得包含进程正常执行和出错时输出内容的输入流。以下示例:windows
public void startProcessNormal() throws IOException { ProcessBuilder pb = new ProcessBuilder("cmd.exe","/c","netstat","-a"); Process process = pb.start(); InputStream input = process.getInputStream(); Files.copy(input,Paths.get("netstat.txt"),StandardCopyOption.REPLACE_EXISTING); }
使用管道的方式在某些状况下显得不够灵活,所以java7对进程的输入和输出处理进行了更新,增长了另外的两种处理方式。第一种是继承式,即新建立进程的输入和输出与当前的java进程相同。第二种是基于文件式,即把文件做为进程输入的来源和输出的目的地。下面的代码给出了继承式的一个示例:微信
package test; import java.io.IOException; import java.lang.ProcessBuilder.Redirect; public class Process2 { public static void main(String[] args) { try { startProcessNormal(); } catch (IOException e) { e.printStackTrace(); } } public static void startProcessNormal() throws IOException { ProcessBuilder pb = new ProcessBuilder("cmd.exe","/c","dir"); pb.redirectOutput(Redirect.INHERIT); //默认输出到控制台 //pb.redirectOutput(new File("F:/ddd.txt")); //输出到文件 pb.start(); } }
上例中,启动的进程经过windows上的命令行工具来执行dir命令,经过ProcessBuilder类的redirectOutput方法把进程的输出设置为继承自父进程,运行的结果就是dir命令的输出内容,会显示在Java程序默认的输出控制台中。app
若是但愿把进程的输入或输出改成文件,那么可使用ProcessBuilder类中的redirectInput和redirectOut方法的其余重载形式。见下面的代码:工具
public void listProcesses() throws IOException { ProcessBuilder pb = new ProcessBuilder("wmic","process"); File output = Paths.get("tasks.txt").toFile(); pb.redirectOutput(output); pb.start(); }
上面的代码给出了基于文件式处理方式的示例。示例中经过一个文件来保存进程的输出内容,只须要把一个java.io.File类的对象做为redirectOutput方法的参数便可。这种作法在实现上至关于经过管道方式读取输入流来获取进程的输出,而后将其写入一个文件中。不过以标准API的方式给出的实现,显然比程序本身来实现要好。ui
从API的角度来讲,java7经过新增的ProcessBuilder.Redirect类对进程的输入和输出重定向方式进行了统一。ProcessBuilder.Redirect类提供了两种直接使用的重定向类型,一种是Java7以前就有的管道式,用ProcessBuilder.Redirect.PIPE来表示;另外一种是前面介绍的继承式,用ProcessBuilder.Redirect.INHERIT来表示。其他3种方式都是与文件相关的,在使用时都须要一个File类的对象做为参数。ProcessBuilder.Redirect.from表示从一个文件中读取内容做为输入,ProcessBuilder.Redirect.to表示把输出写入一个文件中,ProcessBuilder.Redirect.appendTo表示把输出的内容添加到一个已有的文件中。spa
咱们来看看几个示例:操作系统
package test; import java.io.File; import java.io.IOException; import java.lang.ProcessBuilder.Redirect; public class Process3 { public static void main(String[] args) { try { startProcessNormal(); } catch (IOException e) { e.printStackTrace(); } } public static void startProcessNormal() throws IOException { File dir = new File("F:/apktool1.5.2"); ProcessBuilder pb = new ProcessBuilder("cmd.exe","/c","apktool", "d","F:/apk/1-page/1.微信.apk","F:/weixin/"); pb.directory(dir); pb.redirectError(Redirect.INHERIT); //继承方式,信息会打印到标准输出 pb.redirectInput(Redirect.INHERIT); //继承方式,信息会打印到标准输出 pb.start(); } }
例二:命令行
package test; import java.io.File; import java.io.IOException; public class Process3 { public static void main(String[] args) { try { startProcessNormal(); } catch (IOException e) { e.printStackTrace(); } } public static void startProcessNormal() throws IOException { File dir = new File("F:/apktool1.5.2"); ProcessBuilder pb = new ProcessBuilder("cmd.exe","/c","apktool","d", "F:/apk/1-page/1.微信.apk","F:/weixin/"); pb.directory(dir); pb.redirectInput(new File("F:/info.txt")); pb.redirectError(new File("F:/error.txt")); pb.start(); } }
注:遇到管道符|等状况,就会报错!解决办法就是在原有命令前加sh -c ,把原有的命令当成一个完整的参数,如:code
ProcessBuilder pb = new ProcessBuilder("sh","-c","strings -a libssl.so | grep -i OpenSSl");