不,那不是正确的方法。
首先,在某些系统上,您的代码将
gpgProcess.waitFor()永远停留在调用中,因为该过程要等到其标准输出和标准错误被完全读取并使用后才能完成。
其次,您没有正确使用Reader的ready()方法。该文件指出,只有当读取一个字符保证不被阻塞的方法返回true。返回false
并不 意味着已经到达流的末尾; 而是 返回false 。这只是意味着下一次读取可能会阻塞(意味着它可能不会立即返回)。
知道何时到达阅读器数据流末尾的 唯一 方法是:
- 检查其任何
read
方法是否返回负数 - 检查
readLine
BufferedReader 的方法是否返回null
因此,您的readStream方法应如下所示:
String line;while ((line = reader.readLine()) != null) { result.append(line).append("n");}从Java 8开始,您可以使其更短:
return reader.lines().collect(Collectors.joining("n"));同样,您不应致电
stdErr.ready()或
stdOut.ready()。即使没有可用的字符,这两个方法中的一个或两个都可能返回true或不返回true;ready()方法的唯一保证是返回true意味着下次读取不会阻塞。只要下一次读取将立即返回-1(即使该读取不被阻塞),即使在字符流的末尾,ready()也可能返回true。
总之,根本不要使用ready()。消耗所有两个流,并检查错误流是否为空:
String output = readStream(stdErr);if (output.isEmpty()) { String output = readStream(stdOut);}gpgResult = "Exit pre: " + gpgProcess.exitValue() + "n" + output;这将解决您的问题似乎出现的情况:或者过程产生标准错误,并且标准输出上没有行,或者反之。但是,这通常无法正确处理流程。
对于一般情况,最简单的解决方案是使进程使用redirectErrorStream将其标准错误与标准输出合并,因此只消耗一个流:
processBuilder.redirectErrorStream(true);Process gpgProcess = processBuilder.start();
然后,verifyExecution方法可以包含:
String output;try (BufferedReader stdOut = new BufferedReader(new InputStreamReader(gpgProcess.getInputStream()))) { output = readStream(stdOut);}if (output.isEmpty()) { gpgResult = "Exit pre: " + gpgProcess.waitFor();} else { gpgResult = "Exit pre: " + gpgProcess.waitFor() + "n" + output;}如果绝对必须有单独的标准错误和标准输出,则至少需要一个后台线程。我发现ExecutorService使从后台线程传递值更加容易:
ExecutorService background = Executors.newSingleThreadExecutor();Future<String> stdOutReader = background.submit(() -> readStream(stdOut));String output = readStream(stdErr);if (output.isEmpty()) { output = stdOutReader.get();}background.shutdown();if (output.isEmpty()) { gpgResult = "Exit pre: " + gpgProcess.waitFor();} else { gpgResult = "Exit pre: " + gpgProcess.waitFor() + "n" + output;}最后,您不应仅为了打印出来就捕获并重新抛出IOException。无论如何,任何代码调用
verifyExecution都必须捕获IOException;代码的工作就是打印,记录或以其他方式处理IOException。这样拦截它可能会导致其打印两次。



