Java - 读取另一个线程正在写入 stdout/stderr 的内容 - 如何?

发布于 2024-11-16 22:58:17 字数 955 浏览 1 评论 0原文

我有一个函数“a()”,它调用另一个写入标准输出的函数“b()”。我无法修改“b()”,但我希望能够读取“b”正在写入的内容并写回标准输出以供“b”读取,这意味着:

public void a() {
    // start a thread that listens to stdout.
    // the thread should print a name to stdout after "b" print "Please enter your name"
    b();
}

public void b() { // I cannot modify this function
   System.out.println("Welcome! The time is " + System.currentTimeMillis());
   System.out.println("Please enter your name");
   String name = ...
   // ... b reads here the name that the thread from function a() will write
   // ...
   System.out.println("This is the name that was entered: " + name);
}

我想过在新进程中启动“b”,但我不确定如何,除非我将“b”包装在主函数中并使用命令行运行它 - 我很乐意提供建议。 如果它不是一个进程,我不知道如何实现将由“a()”激活的线程。

我尝试使用:

BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
String line;
while ((line = stdin.readLine()) != null) {
...
}

但它没有捕获“b”正在写的内容。

感谢您的帮助

I have a function "a()" that calls another function "b()" that writes to stdout. I cannot modify "b()", but I want to be able to read what "b" is writing and write back to stdout for "b" to read, meaning:

public void a() {
    // start a thread that listens to stdout.
    // the thread should print a name to stdout after "b" print "Please enter your name"
    b();
}

public void b() { // I cannot modify this function
   System.out.println("Welcome! The time is " + System.currentTimeMillis());
   System.out.println("Please enter your name");
   String name = ...
   // ... b reads here the name that the thread from function a() will write
   // ...
   System.out.println("This is the name that was entered: " + name);
}

I thought about starting "b" in a new process but I wasn't sure how unless I wrap "b" in a main function and run it using a command line - I'd be happy for suggestions.
If it's not a process, I'm not sure how to implement the thread that will be activated by "a()".

I tried using:

BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
String line;
while ((line = stdin.readLine()) != null) {
...
}

but it doesn't catch what "b" is writing.

Thanks for the help

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

郁金香雨 2024-11-23 22:58:17

您可以在另一个进程中运行 b(),但您不需要这样做。

System.out 是一个打印流。如果您仔细阅读 javadoc会注意到System.setOut方法。有了它,您可以用另一个 PrintStream 替换 System.out。

示例(未测试):

PrintStream originalOut = System.out; // To get it back later
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream newOut = new PrintStream(baos);
System.setOut(newOut);

b();
System.out.flush();

System.setOut(originalOut); // So you can print again

ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());

// Now you can read from bais what b() wrote to System.out

该解决方案存在线程不安全的问题。如果任何其他线程在“更改”时写入 System.out,则输出也将被重定向。为了摆脱这个问题,我认为您需要在另一个 JVM 上运行 b() 或使用 PrintStream 根据线程或上下文拆分(解复用)输出。

You can run b() in another process but you don't need to do so.

System.out is a PrintStream. If you read the javadoc carefully you will notice System.setOut method. With it you can replace System.out with another PrintStream.

Example (not tested):

PrintStream originalOut = System.out; // To get it back later
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream newOut = new PrintStream(baos);
System.setOut(newOut);

b();
System.out.flush();

System.setOut(originalOut); // So you can print again

ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());

// Now you can read from bais what b() wrote to System.out

This solution has the problem of not being thread safe. If any other thread write to System.out when it is 'changed' the output will get redirected too. To get rid of this problem I think you need to run b() on another JVM or use a PrintStream that split (deMux) the output depending on the thread or context.

纸伞微斜 2024-11-23 22:58:17

不幸的是,在 Java 中没有简单的方法可以做到这一点。最大的问题是 System.out 和 System.in 是两个独立的文件,分别由 FileDescriptor.out 和 FileDescriptor.in 创建分别。它们没有以任何方式连接,因此您无法写入 System.out 并期望在 System.in 中看到它。

您的选择是:

  1. 以某种方式在外部进程中运行b()。是的,您需要将其放入具有 main() 函数的类中,并进行大量复杂的进程设置,例如获取 java.exe 的路径并进行设置好的部分是写入和读取进程将按照您的预期工作。

  2. 创建两个自定义输入和输出流,可以将所有流量复制到另一个输入/输出流,并将其发送到 System.{in,out},并使用 设置它们System.set{输入,输出}。这样,您就可以监视这些流,而不会影响可能使用 System.{in,out} 的其他代码。

作为 2 中提到的自定义 OutputStream 的示例,请尝试这样的操作:

class CopyOutputStream extends OutputStream {
    private final OutputStream str1;
    private final OutputStream str2;

    public CopyOutputStream(OutputStream str1, OutputStream str2) {
        this.str1 = str1;
        this.str2 = str2;
    }

    @Override
    public void write(int b) throws IOException {
        str1.write(b);
        str2.write(b);
    }

    // ...

    @Override
    public void close() throws IOException {
        try {
            str1.close();
        } finally {
            str2.close();
        }
    }
}

// then in a() do

public void a(){
    //Create a pipe to capture data written to System.out
    final PipedInputStream pipeIn = new PipedInputStream();
    final PipedOutputStream pipeOut = new PipedOutputStream(pipeIn);

    OutputStream out = new CopyOutputStream(System.out, pipeOut);
    //From now on everything written to System.out will be sent to
    // System.out first and then copied to pipeOut and will be available
    // to read from pipeIn.
    System.setOut(new PrintStream(out));

    // In another thread: read data from System.out
    BufferedReader reader = new BufferedReader(new InputStreamReader(pipeIn));
    String name = reader.readLine();
}

不幸的是,您将不得不对 System.in 重复上述过程,这意味着更疯狂的代码,但我不认为事情会比这更容易。

如果您准备好进行一些非常疯狂的操作,也许您可​​以获取一些 java 库(最有可能使用本机代码),它可以为您提供当前运行的 JVM 的 Process 对象,然后使用get{Input,Output}Stream() 方法来完成这项工作。

Unfortunately there is not easy way of doing this in Java. The biggest problem is that System.out and System.in are two separate files, created from FileDescriptor.out and FileDescriptor.in respectively. They are not connected in any way, and therefore you can't write to System.out and expect to see it in System.in.

Your options are:

  1. Run b() in external process somehow. Yes, you'll need to put it in a class with main() function and do lots of complicated process setup, like getting the path to java.exe and setting up classpaths etc. The good part is that writing to and reading from the process will work as you expect.

  2. Create two custom Input and Output streams that can duplicate all traffic to another in/out stream, as well as sending it to System.{in,out}, and set them using System.set{In,Out}. This way you can monitor those streams without affecting other code that might by using System.{in,out}.

As an example of the custom OutputStream mentioned in 2, try something like this:

class CopyOutputStream extends OutputStream {
    private final OutputStream str1;
    private final OutputStream str2;

    public CopyOutputStream(OutputStream str1, OutputStream str2) {
        this.str1 = str1;
        this.str2 = str2;
    }

    @Override
    public void write(int b) throws IOException {
        str1.write(b);
        str2.write(b);
    }

    // ...

    @Override
    public void close() throws IOException {
        try {
            str1.close();
        } finally {
            str2.close();
        }
    }
}

// then in a() do

public void a(){
    //Create a pipe to capture data written to System.out
    final PipedInputStream pipeIn = new PipedInputStream();
    final PipedOutputStream pipeOut = new PipedOutputStream(pipeIn);

    OutputStream out = new CopyOutputStream(System.out, pipeOut);
    //From now on everything written to System.out will be sent to
    // System.out first and then copied to pipeOut and will be available
    // to read from pipeIn.
    System.setOut(new PrintStream(out));

    // In another thread: read data from System.out
    BufferedReader reader = new BufferedReader(new InputStreamReader(pipeIn));
    String name = reader.readLine();
}

Unfortunately you will have to repeat the above process for System.in, which means more crazy code, but I don't think it will get easier than this.

If you are ready for some really crazy action, maybe you can get hold of some java library (most likely with native code), that can give you the Process object of the currently running JVM, and then use get{Input,Output}Stream() methods to do the job.

对你而言 2024-11-23 22:58:17

如何使用 System.setOut 设置 System.out

How about setting System.out with System.setOut

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文