如何将进程的输入/输出流复制到其系统对应项?

7
这是对这个问题的跟进。那里的答案建议将进程的输出、错误和输入流复制到系统版本中,使用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”命令都没有响应。
所以我的问题是:你能否建议一种替代方法来复制流,以便与交互式进程一起使用?
3个回答

5
问题在于IOUtil.copy()在仍有数据需要复制的情况下运行。由于您的进程只是不时地生成数据,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中的所有可用字节数并将它们全部复制到output中。在内部,InputStream会将线程置于wait()状态,当数据可用时再唤醒线程。
因此,在这种情况下,这是尽可能高效的方法。

1
谢谢您的建议。然而,IOUtil.copy() 直到流结束时才退出,因此它与您的代码没有太大区别,两者在处理 sh 时存在相同的问题。 - vitaut
请查看此链接:https://dev59.com/jnRA5IYBdhLWcg3w2xsI#808795 - Peter Knego

2

Process.getOutputStream() 返回一个 BufferedOutputStream ,所以如果你想让输入真正传递到子进程,你需要在每个 write() 后调用 flush()

你也可以重写你的示例,在一个线程中完成所有操作(尽管它使用轮询同时读取 System.in 和进程的 stdout):

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());
  }
}

1
你应该使用ProcessBuilder.redirectOutput方法及其相关方法。在这里阅读更多。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接