|
|
@@ -0,0 +1,67 @@
|
|
|
+package space.anyi.process;
|
|
|
+
|
|
|
+import java.io.BufferedReader;
|
|
|
+import java.io.File;
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.InputStream;
|
|
|
+import java.io.InputStreamReader;
|
|
|
+import java.io.OutputStream;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.nio.file.Paths;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 示例二:获取子进程的标准输入 / 标准输出 / 标准错误。
|
|
|
+ *
|
|
|
+ * 子进程为 src/main/resources/code/io/Main.java:
|
|
|
+ * - 从标准输入逐行读取并回显到标准输出;
|
|
|
+ * - 读到 EOF 后向标准错误打印统计信息。
|
|
|
+ * 主进程演示三件事:写入标准输入、读取标准输出、读取标准错误。
|
|
|
+ */
|
|
|
+public class ProcessIOExample {
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(ProcessIOExample.class);
|
|
|
+
|
|
|
+ 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();
|
|
|
+
|
|
|
+ // 标准输出、标准错误分别在后台线程读取:
|
|
|
+ // 1) 避免阻塞当前线程;2) 防止子进程输出超过管道缓冲区(约 64KB)时写阻塞、无法退出
|
|
|
+ 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();
|
|
|
+
|
|
|
+ boolean exited = process.waitFor(5, TimeUnit.SECONDS);
|
|
|
+ log.info("子进程是否已退出: {}", exited);
|
|
|
+ log.info("子进程退出状态码: {}", process.exitValue());
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 起一个后台线程把给定输入流按行读取并打日志,读到 EOF(子进程退出)后结束 */
|
|
|
+ private static Thread drain(String tag, InputStream in) {
|
|
|
+ Thread thread = new Thread(
|
|
|
+ () -> new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))
|
|
|
+ .lines()
|
|
|
+ .forEach(line -> log.info("[{}] {}", tag, line)),
|
|
|
+ "drain-" + tag);
|
|
|
+ thread.setDaemon(true);
|
|
|
+ thread.start();
|
|
|
+ return thread;
|
|
|
+ }
|
|
|
+}
|