|
|
@@ -14,43 +14,72 @@ import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
|
/**
|
|
|
- * 示例二:获取子进程的标准输入 / 标准输出 / 标准错误。
|
|
|
+ * 示例二:获取子进程的输入和输出(标准输入 / 标准输出 / 错误输出)。
|
|
|
*
|
|
|
- * 子进程为 src/main/resources/code/io/Main.java:
|
|
|
- * - 从标准输入逐行读取并回显到标准输出;
|
|
|
- * - 读到 EOF 后向标准错误打印统计信息。
|
|
|
- * 主进程演示三件事:写入标准输入、读取标准输出、读取标准错误。
|
|
|
+ * 三个案例点各用一个 method 演示:
|
|
|
+ * <ul>
|
|
|
+ * <li>standardOutput() —— 标准输出</li>
|
|
|
+ * <li>standardError() —— 错误输出</li>
|
|
|
+ * <li>standardInput() —— 标准输入</li>
|
|
|
+ * </ul>
|
|
|
+ * 子进程为 src/main/resources/code/io/Main.java。
|
|
|
*/
|
|
|
public class ProcessIOExample {
|
|
|
private static final Logger log = LoggerFactory.getLogger(ProcessIOExample.class);
|
|
|
+ private static final File WORK_DIR = Paths.get(System.getProperty("user.dir"),
|
|
|
+ "src", "main", "resources", "code", "io").toFile();
|
|
|
|
|
|
public static void main(String[] args) throws IOException, InterruptedException {
|
|
|
- File workDir = Paths.get(System.getProperty("user.dir"), "src", "main", "resources", "code", "io").toFile();
|
|
|
- Jdk.compileFixture(workDir);
|
|
|
-
|
|
|
- // 启动子进程(不调用 redirectErrorStream,三条流保持独立)
|
|
|
- Process process = new ProcessBuilder(Jdk.java(), "-Dfile.encoding=UTF-8", "Main")
|
|
|
- .directory(workDir)
|
|
|
- .start();
|
|
|
+ Jdk.compileFixture(WORK_DIR);
|
|
|
+ standardOutput();
|
|
|
+ standardError();
|
|
|
+ standardInput();
|
|
|
+ }
|
|
|
|
|
|
- // 标准输出、标准错误分别在后台线程读取:
|
|
|
- // 1) 避免阻塞当前线程;2) 防止子进程输出超过管道缓冲区(约 64KB)时写阻塞、无法退出
|
|
|
+ /** 案例点:标准输出。子进程启动即打印一行 stdout,读取 getInputStream() 即可获得 */
|
|
|
+ private static void standardOutput() throws IOException, InterruptedException {
|
|
|
+ Process process = startChild();
|
|
|
+ // 后台线程读取标准输出,避免阻塞当前线程
|
|
|
Thread stdoutDrain = drain("stdout", process.getInputStream());
|
|
|
+ // 不写入任何 stdin,直接关闭输入流,让子进程读到 EOF 后退出
|
|
|
+ process.getOutputStream().close();
|
|
|
+ stdoutDrain.join();
|
|
|
+ process.waitFor(5, TimeUnit.SECONDS);
|
|
|
+ log.info("退出状态码: {}", process.exitValue());
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 案例点:错误输出。子进程在 stdin 读到 EOF 后打印一行 stderr,读取 getErrorStream() 即可获得 */
|
|
|
+ private static void standardError() throws IOException, InterruptedException {
|
|
|
+ Process process = startChild();
|
|
|
Thread stderrDrain = drain("stderr", process.getErrorStream());
|
|
|
+ process.getOutputStream().close();
|
|
|
+ stderrDrain.join();
|
|
|
+ process.waitFor(5, TimeUnit.SECONDS);
|
|
|
+ log.info("退出状态码: {}", process.exitValue());
|
|
|
+ }
|
|
|
|
|
|
- // 向子进程标准输入写入数据;try-with-resources 关闭输入流即表示 EOF(没有更多输入)
|
|
|
+ /** 案例点:标准输入。向 getOutputStream() 写入数据即为子进程的 stdin,子进程回显到 stdout */
|
|
|
+ private static void standardInput() throws IOException, InterruptedException {
|
|
|
+ Process process = startChild();
|
|
|
+ // stdout、stderr 都在后台线程读取,避免管道写满导致子进程阻塞
|
|
|
+ Thread stdoutDrain = drain("stdout", process.getInputStream());
|
|
|
+ Thread stderrDrain = drain("stderr", process.getErrorStream());
|
|
|
+ // 写入两行数据;try-with-resources 关闭输入流即表示 EOF
|
|
|
try (OutputStream stdin = process.getOutputStream()) {
|
|
|
stdin.write("第一行输入\r\n".getBytes(StandardCharsets.UTF_8));
|
|
|
stdin.write("第二行输入\r\n".getBytes(StandardCharsets.UTF_8));
|
|
|
}
|
|
|
-
|
|
|
- // 后台读取线程在子进程写满/结束后读到 EOF 才结束,join 后子进程必然已经退出
|
|
|
stdoutDrain.join();
|
|
|
stderrDrain.join();
|
|
|
+ process.waitFor(5, TimeUnit.SECONDS);
|
|
|
+ log.info("退出状态码: {}", process.exitValue());
|
|
|
+ }
|
|
|
|
|
|
- boolean exited = process.waitFor(5, TimeUnit.SECONDS);
|
|
|
- log.info("子进程是否已退出: {}", exited);
|
|
|
- log.info("子进程退出状态码: {}", process.exitValue());
|
|
|
+ private static Process startChild() throws IOException {
|
|
|
+ return new ProcessBuilder(Jdk.java(), "-Dfile.encoding=UTF-8", "Main")
|
|
|
+ .directory(WORK_DIR)
|
|
|
+ .redirectErrorStream(false)
|
|
|
+ .start();
|
|
|
}
|
|
|
|
|
|
/** 起一个后台线程把给定输入流按行读取并打日志,读到 EOF(子进程退出)后结束 */
|