【问题标题】:How to copy input/output streams of the Process to their System counterparts?如何将流程的输入/输出流复制到其系统对应物?
【发布时间】:2011-05-09 19:42:17
【问题描述】:

这是对this question 的跟进。建议的答案是

将进程输出、错误和输入流复制到系统版本

IOUtils.copy如下(修复各种编译错误后):

import org.apache.commons.io.IOUtils;
import java.io.IOException;

public class Test {
    public static void main(String[] args)
            throws IOException, InterruptedException {
        final Process process = Runtime.getRuntime().exec("/bin/sh -i");
        new Thread(new Runnable() {public void run() {
            try {
                IOUtils.copy(process.getInputStream(), System.out);
            } catch (IOException e) {}
        } } ).start();
        new Thread(new Runnable() {public void run() {
            try {
                IOUtils.copy(process.getErrorStream(), System.err);
            } catch (IOException e) {}
        } } ).start();
        new Thread(new Runnable() {public void run() {
            try {
                IOUtils.copy(System.in, process.getOutputStream());
            } catch (IOException e) {}
        } } ).start();
        process.waitFor();
    }
}

但是,生成的代码不适用于执行sh -i 命令的交互式进程。在后一种情况下,任何sh 命令都没有响应。

所以我的问题是:您能否建议一种替代方法来复制适用于交互式流程的流?

【问题讨论】:

    标签: java process io stream pipe


    【解决方案1】:

    问题是 IOUtil.copy() 正在运行,而 InputStream 中有要复制的数据。由于您的进程只是不时地产生数据,IOUtil.copy() 认为没有要复制的数据而退出。

    只需手动复制数据并使用布尔值将线程形式停止在外面:

    byte[] buf = new byte[1024];
    int len;
    while (threadRunning) {  // threadRunning is a boolean set outside of your thread
        if((len = input.read(buf)) > 0){
            output.write(buf, 0, len);
        }
    }
    

    这会读取 inputStream 上可用的字节数,并将所有字节复制到输出。 InputStream 在内部将线程放入wait(),然后在数据可用时将其唤醒。
    因此,在这种情况下,它会尽可能高效。

    【讨论】:

    • 感谢您的建议。但是,IOUtil.copy() 直到到达流的末尾才会退出,因此它与您的代码没有太大的不同,后者与 sh 有相同的问题。
    【解决方案2】:

    Process.getOutputStream() 返回一个BufferedOutputStream,所以如果你想让你的输入真正到达子进程,你必须在每个write() 之后调用flush()

    您还可以重写您的示例以在一个线程上执行所有操作(尽管它使用轮询来同时读取 System.in 和进程的标准输出):

    import java.io.*;
    
    public class TestProcessIO {
    
      public static boolean isAlive(Process p) {
        try {
          p.exitValue();
          return false;
        }
        catch (IllegalThreadStateException e) {
          return true;
        }
      }
    
      public static void main(String[] args) throws IOException {
        ProcessBuilder builder = new ProcessBuilder("bash", "-i");
        builder.redirectErrorStream(true); // so we can ignore the error stream
        Process process = builder.start();
        InputStream out = process.getInputStream();
        OutputStream in = process.getOutputStream();
    
        byte[] buffer = new byte[4000];
        while (isAlive(process)) {
          int no = out.available();
          if (no > 0) {
            int n = out.read(buffer, 0, Math.min(no, buffer.length));
            System.out.println(new String(buffer, 0, n));
          }
    
          int ni = System.in.available();
          if (ni > 0) {
            int n = System.in.read(buffer, 0, Math.min(ni, buffer.length));
            in.write(buffer, 0, n);
            in.flush();
          }
    
          try {
            Thread.sleep(10);
          }
          catch (InterruptedException e) {
          }
        }
    
        System.out.println(process.exitValue());
      }
    }
    

    【讨论】:

      【解决方案3】:

      您应该改用ProcessBuilder.redirectOutput 方法和朋友。阅读更多here

      【讨论】:

        猜你喜欢
        • 2014-01-05
        • 1970-01-01
        • 2010-12-07
        • 2015-05-14
        • 2012-09-06
        • 2011-08-12
        • 1970-01-01
        • 2017-05-19
        • 2015-11-06
        相关资源
        最近更新 更多