如何将stdin重定向到多个java进程,同时接收输出

rqcrx0a6  于 2021-06-29  发布在  Java
关注(0)|答案(1)|浏览(482)

我正在尝试编写一个java程序来启动多个进程,并将stdin重定向到主程序,同时将这些进程的输出读取到stdout。
目前我正试图启动 vlc 进程和以后计划使用不同的键盘键分别控制它们,但我想了解如何使其适用于任何进程,例如命令行或自定义telnet客户端,这样您就可以生成两个命令行并向它们发送相同的命令输入,或者以编程方式替换每个进程的部分命令。
我遇到的问题是,无法预测何时发送哪一端,所以我需要一个双向管道,或者两个管道同时运行。我还没能想出如何做到这一点,我所做的一切都让它挂起等待来自任何一种方式的输入。
我可以启动两个 vlc 进程,甚至读取它们的两个输出,但是如果进程没有挂起等待输入,我就不能将stdin发送给它们。
我已经知道了:
父类:

package multiVLC;

    import java.nio.file.Path;
    import java.nio.file.Paths;

    public class multiVLC {
        public static void main(String[] args) {
            Path video1 = Paths.get("C:\\path\\to\\video1.mkv");
            Path video2 = Paths.get("C:\\path\\to\\video2.mkv");
            new VLCProcess(video1).start();
            new VLCProcess(video2).start();
        }
    }

以及过程:

package multiVLC;

import java.io.*;
import java.nio.file.Path;
import java.util.Scanner;

public class VLCProcess extends Thread {

    private Path videofile;

    public VLCProcess(Path videofile) {
        this.videofile = videofile;
    }

    public void run() {
        try {
            String[] cmd = {"C:\\Program Files\\VideoLAN\\VLC\\vlc.exe", videofile.toRealPath().toString()};
            ProcessBuilder ps = new ProcessBuilder(cmd);
            ps.redirectErrorStream(true); // combine stdErr and stdOut
            Process pr = ps.start();

            BufferedReader in = new BufferedReader(new InputStreamReader(pr.getInputStream()));
            BufferedWriter out = new BufferedWriter(new OutputStreamWriter(pr.getOutputStream()));
            BufferedReader sysin = new BufferedReader(new InputStreamReader(System.in));

            String line;

            Scanner scanin = new Scanner(in);
            Scanner scansysin = new Scanner(sysin);

            while (true) {  // THIS HANGS
                if (scanin.hasNextLine()) {
                    System.out.println(this.getName() + ":" + scanin.nextLine());
                }
                if (scansysin.hasNextLine()) {
                    out.write(scansysin.nextLine());
                }
                System.out.println("still in loop"); // NEVER PRINTS
            }

//            while ((line = in.readLine()) != null) { // THIS WORKED TO RUN TWO VLC PROCESSES, BUT DOESN'T REDIRECT STDIN
//                System.out.println(this.getName() + ":" + line);
//            }
//            pr.waitFor(); // not sure if this is needed

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

如何正确处理两种方式的管道?

66bbxpm5

66bbxpm51#

要将程序的输入重定向到多个进程的输出,可以尝试以下代码,希望这些代码能够满足您的要求:

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Objects;
import java.util.Scanner;

public class Main {

    private static final Charset CHARSET = StandardCharsets.UTF_8; //Make sure you use the proper charset.

    public static class StartedProcess implements Runnable {

        private final BufferedWriter out;
        private final BufferedReader in;
        private final Process pro;

        public StartedProcess(final Process pro) {
            this.pro = Objects.requireNonNull(pro);
            out = new BufferedWriter(new OutputStreamWriter(pro.getOutputStream(), CHARSET));
            in = new BufferedReader(new InputStreamReader(pro.getInputStream(), CHARSET));
            //err = new BufferedReader(new InputStreamReader(pro.getErrorStream(), CHARSET));
        }

        @Override
        public void run() {
            final String n = Thread.currentThread().getName();
            try {
                for (String line = in.readLine(); line != null; line = in.readLine())
                    System.out.println(n + ": " + line);
                System.out.println(n + " stream closed.");
            }
            catch (final IOException iox) {
                System.out.println(n + " stream error: " + iox);
            }
        }

        public Process getProcess() {
            return pro;
        }

        public BufferedWriter getOutput() {
            return out;
        }

        //public BufferedReader getError() {
        //    return err;
        //}
    }

    private static StartedProcess startProcess(final String... command) throws IOException {
        final ProcessBuilder pb = new ProcessBuilder(command);
        pb.redirectErrorStream(true);
        final StartedProcess sp = new StartedProcess(pb.start());
        new Thread(sp).start(); //Probably you could better utilize an ExecutorService here.
        return sp;
    }

    private static String pathToString(final String pathFirst, final String... pathMore) {
        return Paths.get(pathFirst, pathMore).toString();
    }

    public static void main(final String[] args) throws IOException { //You should handle this exception appropriately.
        final String vlc = pathToString("path1", "to", "vlc");
        final String mv1 = pathToString("path2", "to", "movie1.mkv");
        final String mv2 = pathToString("path3", "to", "movie2.mkv");

        System.out.println("Starting processes...");

        final ArrayList<StartedProcess> processes = new ArrayList<>(Arrays.asList(
                startProcess(vlc, mv1),
                startProcess(vlc, mv2)
        ));

        final Scanner scan = new Scanner(System.in, CHARSET.name());

        while (!processes.isEmpty()) {
            System.out.println("Waiting for your input...");
            final String line = scan.nextLine();
            final Iterator<StartedProcess> procit = processes.iterator();
            while (procit.hasNext()) {
                try {
                    final BufferedWriter out = procit.next().getOutput();
                    out.write(line);
                    out.newLine();
                    out.flush();
                }
                catch (final IOException iox) {
                    procit.remove();
                }
            }
        }

        System.out.println("No processes left.");
    }
}

基本上,程序的主线程负责获取用户输入,然后通过一个简单的循环将用户的输入转发到所有存储进程的输出。
每个派生进程的输入都在它自己的线程中处理,因此您应该确保同步它们,以防它们之间共享资源。 System.out 确实是在进程之间共享的,因为在这段代码中,我只是将所有进程的输入打印到它,但不用担心,因为 PrintStream 的方法已经同步,所以您不会得到字节交错,但您将得到行交错。
你不需要导出 InputStream 每个的 StartedProcess 我建议你不要这样做,因为它是在 Runnable 这就是它的目的。
您可以将其编码为 ExecutorService 对于 Runnable s、 以及如何处理 Exception 这是适当的。

编辑1:

我忘了提到,您可能会将输入与进程的输出交织在一起,这是因为当您在cli中键入时,进程将同时向其写入。

相关问题