Procházet zdrojové kódy

示例二:标准输入/输出/错误各拆为独立 method

yangyi před 1 týdnem
rodič
revize
667fc7e326

+ 49 - 20
src/main/java/space/anyi/process/ProcessIOExample.java

@@ -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(子进程退出)后结束 */

+ 6 - 4
src/main/resources/code/io/Main.java

@@ -4,18 +4,20 @@ import java.io.InputStreamReader;
 
 /**
  * 子进程程序:标准输入 / 标准输出 / 标准错误演示程序。
- * 从标准输入逐行读取并回显到标准输出;
- * 读到 EOF(无更多输入)后向标准错误打印统计信息,随后退出。
+ * - 启动时向标准输出打印一行(演示 stdout);
+ * - 从标准输入逐行读取并回显到标准输出(演示 stdin → stdout);
+ * - 读到 EOF(无更多输入)后向标准错误打印统计信息(演示 stderr)。
  */
 public class Main {
     public static void main(String[] args) throws IOException {
+        System.out.println("child: started, waiting for stdin");
         BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
         String line;
         int count = 0;
         while ((line = stdin.readLine()) != null) {
-            System.out.println("echo: " + line);
+            System.out.println("child: echo " + line);
             count++;
         }
-        System.err.println("child: read " + count + " line(s) from stdin");
+        System.err.println("child: stdin EOF after " + count + " line(s)");
     }
 }