Java地带 - Java调用WMIC命令
type: channel
link: https://blog.apires.cn/tag/Java调用WMIC命令/
items: Java调用WMIC命令的方法阻塞

Java调用WMIC命令的方法阻塞
type: item
date: 2012-06-23T21:07:10+08:00
description: 今天要写个远程重启服务的功能，为了开发速度，暂时定为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(); 运行，结果发现程序不能退出，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(); 编译运行，发现还是有问题，依然还是阻塞。又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(); 编译运行，程序成功执行。果然是标准输入的原因。后来执行的过程中换了一个服务的名称，发现执行失败（能够正常退出，但是返回的结果是“无效动作”），但是同样的命令，在命令行中执行成功，而且直接适用Runtime.exec()方法中写入整个命令也能够执行成功，难道是ProcessBuilder的错误，ProcessBuilder构造函数有两个：ProcessBuilder(List<String> command)利用指定的操作系统程序和参数构造一个进程生成器。ProcessBuilder(String... command)利用指定的操作系统程序和参数构造一个进程生成器。找到ProcessBuilder的源代码，发现了对List<String>的解析方法：JDK将List中的所有字符串用空格连接，对list中的每个字符串JDK先判断串中是否包含空格，如果包含空格，用双引号将该字符串引起来，再拼到前面字符串的后面（应该是为了解决路径中包含空格的问题），可恰好Wmic命令的参数中有一段是name="S…