Runtime.getRuntime().exec()执行阻塞和不能执行管道命令的问题
1.不能执行管道命令的处理方式:
windows平台使用 Runtime.getRuntime().exec(String[]{"cmd", "/k", "cmd str"});
linux平台使用 Runtime.getRuntime().exec(String[]{"sh", "-c", "cmd str"});
下面是Process类的完整例子:
String[] cmds = new String[]{"sh", "-c", "cmd str"}; if (System.getProperty("os.name").toLowerCase().contains("windows")) { cmds = new String[]{"cmd", "/k", "cmd str"}; } Process ps = null; try { ps = Runtime.getRuntime().exec(cmds); doInputProcess(ps.getIntputStream()); doErrorProcess(ps.getErrorStream()); doOutProcess(ps.getOutputStream());
// System.out.println("start."); ps.waitFor();
// System.out.println("done."); } catch (Exception e) { // } finally{ try { if(ps != null)
ps.destroy(); } catch (Exception ec) { // } }
2.执行阻塞问题
必须要同时处理完输入流process.getInputStream()和输出流process.getOutputStream(),错误流process.getErrorStream()可以不用考虑,并且每个流都需要工作在不同的线程,否则上例中的ps.waitFor();后面的语句永远不会被执行!
我们可以把上例写个JSP web项目验证一下
新建index.jsp文件放在share项目下,代码如下:
<%@page import="java.io.*" contentType="text/html; charset=UTF-8" %> <%@page import="java.lang.StringBuilder" contentType="text/html; charset=UTF-8" %> <!DOCTYPE html> <html> <head> <title>Runtime.getRuntime().exec()</title> </head> <body> <%! void inputProcess(final InputStream in, final JspWriter out, String name){ //必须是新线程 new Thread(new Runnable() { @Override public void run() { if(in == null) return; try { int a = -1; byte[] b = new byte[2048]; while ((a = in.read(b)) != -1) { out.println(new String(b)); } } catch (Exception e) { } finally{ try { if(in != null ) in.close(); } catch (Exception ec) { } } } }, name).start(); } void doInputProcess(InputStream in, JspWriter out){ inputProcess(in, out, "inputProcess"); } void doErrorProcess(InputStream in, JspWriter out){ inputProcess(in, out, "errProcess"); } void doOutputProcess(OutputStream out){ try { out.close();//直接关闭流 } catch (Exception ec) { } } %> <% String cmd = request.getParameter("cmd"); if(cmd != null && !"".equals(cmd)){ String[] cmds = new String[]{"sh", "-c", cmd}; if (System.getProperty("os.name").toLowerCase().contains("windows")) { cmds = new String[]{"cmd", "/k", cmd}; } Process ps = null; try { ps = Runtime.getRuntime().exec(cmds); out.println("start."); out.println("<pre>"); doInputProcess(ps.getInputStream(), out); doErrorProcess(ps.getErrorStream(), out); doOutputProcess(ps.getOutputStream()); ps.waitFor(); out.println("</pre>"); out.println("done."); } catch (Exception e) { out.println("err."); } finally{ try { if(ps != null) ps.destroy(); } catch (Exception ec) { } } return; } if("true".equals(request.getParameter("c"))){ return; } out.println("<div style='margin: 20px'>虚拟终端:<input id='command' type='text' value='netstat -an' style='width: 250px;border: none;color: red;background-color: black;'/>" + "<a style='color: blue' onclick=\"var m= top.document.getElementById('command').value;if(!m) return false; top.document.getElementById('view-file').setAttribute('src', './index.jsp?cmd=' + encodeURIComponent(m));\" href=\"#\">执行</a>" + "</div>"); out.println("<div style='margin-top: 20px; padding: 5px; height: 500px'>" + "<iframe id='view-file' src='./index.jsp?c=true' height='100%' style='width: 100%; height: 100%' frameborder='0'></iframe>" + "</div>"); %> </body> </html>
浏览器访问当前JSP文件,输入网址:http://localhost:8080/share/index.jsp
正常执行上面的index.jsp脚本,输入一个命令ipa,服务器相应了200,说明后台没被阻塞
当我们将index.jsp中标红的“doOutputProcess(ps.getOutputStream());”这行代码注释掉之后,在执行同一个命令,会发现请求一直处于阻塞状态,done.也没有被打印下来
总结此问题,要想Runtime.getRuntime().exec()这个进程正常退出,不被阻塞需要配合多个线程,并至少关闭ps.getInputStream()和ps.getOutputStream()两个输入输出流