Runtime.getRuntime()。exec(“C:\ cygwin \ bin \ bash.exe”)没有输入读取

时间:2010-09-04 08:07:46

标签: java process runtime cygwin inputstream

我正在尝试执行一个新进程并从Java中读取其输入流。我已成功使用Runtime.getRuntime()。exec(String)来启动和接收来自多个进程的输入。但是,当我尝试在某些其他进程上使用exec时,输入流的read方法会阻塞,并且看起来没有输入。对于某些进程,可能导致输入流为空的原因是什么?具体来说,我想知道为什么bash.exe没有输出任何东西。

我编写了一个JUnit测试用例来演示这个问题:

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import junit.framework.TestCase;

public class TestExec extends TestCase {

    public void testExec() throws IOException {
        List<InputPrinter> threads = new ArrayList<InputPrinter>();

        // Create a process for each of the commands and make sure that
        // it outputs at least one line to its input stream.
        threads.add(testExec("cmd"));
        threads.add(testExec("java"));
        threads.add(testExec("C:/cygwin/bin/vim-nox.exe"));

        // These bottom two fail, even though executing these
        // commands in cmd.exe results in immediate output
        threads.add(testExec("javac"));
        threads.add(testExec("C:/cygwin/bin/bash.exe"));

        // Give the threads a second to execute
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
            fail();
        }

        // Test that each command had input to read
        for(InputPrinter ip : threads) {
            assertTrue(ip.command + " has not read any input", ip.hasRead);
        }
    }

    // Starts a process for the given command and returns an
    // InputPrinter that can be used to check if the process
    // has had an input to read.
    public InputPrinter testExec(String command) throws IOException {
        Process proc = Runtime.getRuntime().exec(command);
        InputStream in = proc.getInputStream();

        InputPrinter ip = new InputPrinter(in, command);
        new Thread(ip).start();

        return ip;
    }

    // Simple Runnable to read from an InputStream. hasRead will be
    // true if at least one input has been read from the stream
    private class InputPrinter implements Runnable {
        InputStream in;
        String command;
        boolean hasRead;

        public InputPrinter(InputStream in, String command) {
            this.in = in;
            this.command = command;
            this.hasRead = false;
        }

        // Loop indefinitely while printing any received input
        public void run() {
            try {
                final byte[] b = new byte[1024];
                while (true) {
                    int n = in.read(b);
                    if (n > 0) {
                        System.out.print(new String(Arrays.copyOf(b, n)));
                        hasRead = true;
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
                fail();
            }
        }
    }

}

编辑:

据我所知,如果程序没有使用stdout或stderr,我在Windows命令提示符下看不到任何内容。当我启动bash进程时,我期待看到的是“bash-3.2 $”,当我打开命令提示符并运行“bash.exe”时,我看到了同样的事情:

Microsoft Windows [Version 6.1.7600]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\cygwin\bin>bash.exe
bash-3.2$

2 个答案:

答案 0 :(得分:2)

无论Java如何,据我所知,只有当它作为脚本运行时,才能将输出(或输入)从/向bash传输,而不是在它作为交互式shell运行时(在这种情况下你只能通过)它的cmd参数)。

换句话说,当您在注释中提到从cmd运行bash时,您会看到输出,但它包含在bash进程中,而不会输出bash发送回父cmd进程。

关于javac进程,它实际上是将输出发送到错误流。尝试从cmd javac 1>nulljavac 2>null开始运行,您将看到不同之处 你看过api here了吗?您可以尝试使用ProcessBuilder并将错误流重定向回主输入流,以这种方式处理流程会更容易。

答案 1 :(得分:2)

一个进程通常不仅有一个而是两个与之关联的输出流。这些是:

  1. stdout,可以使用getInputStream()
  2. 读取
  3. stderr,可以使用getErrorStream()
  4. 读取

    Javac写入stderr,而不是stdout,因此你不会读取它的输出。

    因为不方便阅读它们是不方便的(几年前,我不得不为此写一个额外的线程),他们为系统进程引入了一个新的API,即ProcessBuilder,它允许将stderr重定向到stdout

    只需替换

        Process proc = Runtime.getRuntime().exec(command);
        InputStream in = proc.getInputStream();
    

        ProcessBuilder pb = new ProcessBuilder(command);
        pb.redirectErrorStream(true);
        Process proc = pb.start();
    

    ,添加所需的导入,测试成功:)。