最近在用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(); } } }