Runtime.getRuntime().exec("C:\cygwin\bin\bash.exe") 没有要读取的输入

Posted

技术标签:

【中文标题】Runtime.getRuntime().exec("C:\\cygwin\\bin\\bash.exe") 没有要读取的输入【英文标题】:Runtime.getRuntime().exec("C:\cygwin\bin\bash.exe") has no input to readRuntime.getRuntime().exec("C:\cygwin\bin\bash.exe") 没有要读取的输入 【发布时间】:2011-04-08 04:06:24 【问题描述】:

我正在尝试执行一个新进程并从 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$

【问题讨论】:

很高兴看到人们在问题中使用断言而不是System.out 我认为这是因为程序执行打印而不是 println.. 问题是什么?有什么问题? 【参考方案1】:

一个进程通常不仅有一个,而且有两个与之关联的输出流。它们是:

    stdout,可以用getInputStream()读取 stderr,可以用getErrorStream()读取

Javac 写入标准错误,而不是标准输出,因此您不会读取它的输出。

因为必须同时阅读它们很不方便(几年前,我不得不为此编写一个额外的线程),他们为系统进程引入了一个新的 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();

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

【讨论】:

谢谢,没想到!不幸的是,我仍然在使用 bash.exe 时遇到问题……可能是由于我对程序的工作原理有误解。【参考方案2】:

不管 Java 是什么,据我所知,您只能在 bash 作为脚本运行时才可以将输出(或输入)从/到 bash,而不是当它作为交互式 shell 运行时(在这种情况下,您只能通过cmd参数)。

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

关于 javac 进程,它实际上是将输出发送到错误流。尝试从 cmd javac 1&gt;nulljavac 2&gt;null 运行,您会看到不同之处。 你看过apihere吗?您可以尝试使用 ProcessBuilder 并将错误流重定向回主输入流,这样处理流程会容易得多。

【讨论】:

如果你所说的 bash 是真的,那为什么我可以从命令提示符运行 bash.exe 可执行文件? (请参阅问题进行编辑) 那是因为 bash 在交互模式下运行所以它正在接管 cmd 窗口。它只是看起来好像它正在写入标准输出。尝试从 cmd 运行“编辑”,例如,它是本机 ms-dos 应用程序。它彻底改变了屏幕,但它只是一个基于文本的应用程序,而不是一个图形应用程序 嗯,我似乎不了解处理将输出提供给命令行解释器的方式。我发布了一个新问题:***.com/questions/3645040/…

以上是关于Runtime.getRuntime().exec("C:\cygwin\bin\bash.exe") 没有要读取的输入的主要内容,如果未能解决你的问题,请参考以下文章

Android Runtime.getRuntime().exec

如何与 Runtime.getRuntime().exec(command) 程序进行交互?

在循环中运行“Runtime.getRuntime()。exec()”

Runtime.getRuntime.exec()执行java进程失败

无法从 Java 进程(Runtime.getRuntime().exec() 或 ProcessBuilder)读取 InputStream

Runtime.getRuntime.exec()执行linux脚本导致程序卡死有关问题