在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");