程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java調用外部程序命令

Java調用外部程序命令

編輯:關於JAVA

今天要寫個遠程重啟服務的功能,為了開發速度,暫時定為Java代碼+WMIC命令的方法,簡單的說,就是利用Java調用本機應用程序的方法。涉及到的 Java類有Java.lang包裡面的Runtime、Process、ProcessBuilder三個類,以及wmic中重啟服務的命令。因為之前 也寫過這方面的東西,所以很習慣性的寫出了代碼:

  Process p = Runtime.getRuntime().exec("wmic ...");

  BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));

  String tmp = null;

  while ((tmp = br.readline()) != null) {

  System.out.println(tmp);

  }

  int exitValue = p.waitfor();

  Process p = Runtime.getRuntime().exec("wmic ..."); BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())); String tmp = null; while ((tmp = br.readline()) != null) { System.out.println(tmp); } int exitValue = p.waitfor();

  運行,結果發現程序不能退出,Debug發現程序阻塞在br.readline()中了,強制結束程序,發現重啟服務的命令正常下下去了,去掉程序中獲得標准輸出的地方和獲得返回結果的地方,命令也能正常下去,而且正常退出。

  為什麼程序會阻塞呢?Google了一下,發現了大家的解釋,應該也是比較權威的解釋吧:每個進程都有自己的標准輸入、標准輸出、標准錯誤輸出,對於某些 依賴於OS的進程,可能其輸出緩沖區很小,如果不能及時的讀出(標准輸出、標注錯誤輸出),將導致進程不能正常退出。我的程序中標准輸出已經讀了,顯然原 因不是這個,難道是錯誤輸出緩沖區中的數據沒有讀出導致的?帶著這個疑問,對程序作了一些更改:

  ProcessBuilder pb = new ProcessBuilder("wmic",...);

  pb.redirectErrorStream(true);

  Process p = pb.start();

  BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));

  String tmp = null;

  while ((tmp = br.readline()) != null) {

  System.out.println(tmp);

  }

  int exitValue = p.waitfor();

  ProcessBuilder pb = new ProcessBuilder("wmic",...); pb.redirectErrorStream(true); Process p = pb.start(); BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())); String tmp = null; while ((tmp = br.readline()) != null) { System.out.println(tmp); } int exitValue = p.waitfor();

  編譯運行,發現還是有問題,依然還是阻塞。又google了一下,大家的評論大多還是關於標准輸出和標准錯誤輸出,那這程序應該是沒有問題了。後來在 cmd中敲入wmic的命令,發現wmic命令敲入以後會進入一個自有的提示符中,難道是因為標准輸入的問題。後來又google了一下,驗證了我的猜 想,果然是因為wmic進程會等待標准輸入,而程序中沒有處理標准輸入的地方,是標准輸入阻塞了進程的退出,修改代碼:

  ProcessBuilder pb = new ProcessBuilder("wmic",...);

  pb.redirectErrorStream(true);

  Process p = pb.start();

  p.getOutputStream().close();

  BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));

  String tmp = null;

  while ((tmp = br.readline()) != null) {

  System.out.println(tmp);

  }

  int exitValue = p.waitfor();

  ProcessBuilder pb = new ProcessBuilder("wmic",...); pb.redirectErrorStream(true); Process p = pb.start(); p.getOutputStream().close(); BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream())); String tmp = null; while ((tmp = br.readline()) != null) { System.out.println(tmp); } int exitValue = p.waitfor();

  編譯運行,程序成功執行。果然是標准輸入的原因。

  後來執行的過程中換了一個服務的名稱,發現執行失敗(能夠正常退出,但是返回的結果是“無效動作”),但是同樣的命令,在命令行中執行成功,而且直接適用 Runtime.exec()方法中寫入整個命令也能夠執行成功,難道是ProcessBuilder的錯誤,ProcessBuilder構造函數有兩 個:

  ProcessBuilder(List command)

  利用指定的操作系統程序和參數構造一個進程生成器。

  ProcessBuilder(String... command)

  利用指定的操作系統程序和參數構造一個進程生成器。

  找到ProcessBuilder的源代碼,發現了對List的解析方法:JDK將List中的所有字符串用空格連接,對 list中的每個字符串JDK先判斷串中是否包含空格,如果包含空格,用雙引號將該字符串引起來,再拼到前面字符串的後面(應該是為了解決路徑中包含空格 的問題),可恰好Wmic命令的參數中有一段是name="ServiceName",如果ServiceName中包換空格,JDK就會把 name="service name"的外層加一個雙引號,導致wmic不能解析該命令了。

  問題終於全都解決了,耗費了多半天的時間,不過收獲總是有的,這裡總結一下,在使用Java調用外部命令的時候,一定要注意對標准輸出、標准輸入和錯誤輸 出的處理。對於一般的命令,只需要將標准輸出和錯誤輸出合並,一起讀出來或者在另外的線程中讀出來,而對於一些特殊的命令,還有處理標准輸入。建議即使不 使用標准輸入,先close了,總是不會出錯了。另外,使用ProcessBuilder時要注意它的空格處理方式是否是你想要的,如果不是,就不能用 ProcessBuilder了,直接使用Runtime.exec()就好了。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved