最近在用Java調用ffmpeg的命令,因此記錄下踩到的坑java
若是要在Java中調用shell腳本時,可使用Runtime.exec或ProcessBuilder.start。它們都會返回一個Process對象,經過這個Process能夠對獲取腳本執行的輸出,而後在Java中進行相應處理。例如,下面的代碼shell
try { Process process = Runtime.getRuntime().exec(cmd); process.waitFor(); //do something ... } catch (Exception e) { e.printStackTrace(); }
一般,安全編碼規範中都會指出:使用Process.waitfor的時候,可能致使進程阻塞,甚至死鎖。 那麼這句應該怎麼理解呢?用個實際的例子說明下。安全
使用Java代碼調用shell腳本,執行後會發現Java進程和Shell進程都會掛起,沒法結束。Java代碼 processtest.javabash
try { Process process = Runtime.getRuntime().exec(cmd); System.out.println("start run cmd=" + cmd); process.waitFor(); System.out.println("finish run cmd=" + cmd); } catch (Exception e) { e.printStackTrace(); }
被調用的Shell腳本doecho.shide
#!/bin/bash for((i=0; ;i++)) do echo -n "0123456789" echo $i >> count.log done
方案一: 基於上述分析,只要主進程在waitfor以前,能不斷處理緩衝區中的數據就能夠。由於,咱們能夠再waitfor以前,單獨啓兩個額外的線程,分別用於處理InputStream和ErrorStream就能夠。實例代碼以下:ui
try { final Process process = Runtime.getRuntime().exec(cmd); System.out.println("start run cmd=" + cmd); //處理InputStream的線程 new Thread() { @Override public void run() { BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream())); String line = null; try { while((line = in.readLine()) != null) { System.out.println("output: " + line); } } catch (IOException e) { e.printStackTrace(); } finally { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } }.start(); new Thread() { @Override public void run() { BufferedReader err = new BufferedReader(new InputStreamReader(process.getErrorStream())); String line = null; try { while((line = err.readLine()) != null) { System.out.println("err: " + line); } } catch (IOException e) { e.printStackTrace(); } finally { try { err.close(); } catch (IOException e) { e.printStackTrace(); } } } }.start(); process.waitFor(); System.out.println("finish run cmd=" + cmd); } catch (Exception e) { e.printStackTrace(); }
方案二:那就直接用ProcessBuilder來建立Process對象吧!ProcessBuilder已經給出了這方面的解決方案,可是必需要注意的是ProcessBuilder的redirectErrorStream方法。查API可知曉,redirectErrorStream方法設置爲ture的時候,會將getInputStream(),getErrorStream()兩個流合併,自動會清空流,無需咱們本身處理。若是是false,getInputStream(),getErrorStream()兩個流分開,就必須本身處理,程序以下:編碼
public class ProcessBuilderTest { public static void main(String[] args) { List<String> params = new ArrayList<String>(); params.add("java"); params.add("-jar"); params.add("ProcessJar.jar"); params.add("args1"); params.add("args2"); params.add("args3"); ProcessBuilder processBuilder = new ProcessBuilder(params); // System.out.println(processBuilder.directory()); // System.out.println(processBuilder.environment()); processBuilder.redirectErrorStream(true); try { Process process = processBuilder.start(); BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream())); String line; while ((line = br.readLine()) != null) { System.out.println(line); } int exitCode = process.waitFor(); System.out.println("exitCode = "+exitCode); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }