【问题标题】:Runtime.getRuntime().exec(cmd) hangingRuntime.getRuntime().exec(cmd) 挂起
【发布时间】:2012-10-22 09:34:34
【问题描述】:

我正在执行一个返回文件修订号的命令; '文件名'。但是如果执行命令时出现问题,那么应用程序就会挂起。我能做些什么来避免这种情况?请在下面找到我的代码。

String cmd= "cmd /C si viewhistory --fields=revision --project="+fileName; 
Process p = Runtime.getRuntime().exec(cmd) ;  
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));  
String line = null; 
while ((line = in.readLine()) != null) {  
System.out.println(line);  
} 

} catch (Exception e) {  
e.printStackTrace();  
 }

【问题讨论】:

  • 看看ProcessBuilder。这是做这类事情的一个更简单的 API
  • 如果没有输出,readLine将永远阻塞。
  • @assylias: 我如何检查是否没有输出?
  • 阅读tag Wiki for exec 中链接的文章并遵循建议。还要听从@MyNameIsTooCommon 的建议并使用ProcessBuilder

标签: java process runtime.exec processbuilder


【解决方案1】:

我猜问题是您只读取 InputStream 而没有读取 ErrorStream。您还必须注意并行读取两个流。当前从输出流传输的数据可能会填满操作系统缓冲区,您的 exec 命令将自动暂停,让您的读者有机会清空缓冲区。但程序仍将等待输出处理。因此,发生了挂起。

您可以创建一个单独的类来处理输入和错误流,如下所示,

public class ReadStream implements Runnable {
    String name;
    InputStream is;
    Thread thread;      
    public ReadStream(String name, InputStream is) {
        this.name = name;
        this.is = is;
    }       
    public void start () {
        thread = new Thread (this);
        thread.start ();
    }       
    public void run () {
        try {
            InputStreamReader isr = new InputStreamReader (is);
            BufferedReader br = new BufferedReader (isr);   
            while (true) {
                String s = br.readLine ();
                if (s == null) break;
                System.out.println ("[" + name + "] " + s);
            }
            is.close ();    
        } catch (Exception ex) {
            System.out.println ("Problem reading stream " + name + "... :" + ex);
            ex.printStackTrace ();
        }
    }
}

你的使用方式如下,

String cmd= "cmd /C si viewhistory --fields=revision --project="+fileName; 
Process p = Runtime.getRuntime().exec(cmd) ;  
s1 = new ReadStream("stdin", p.getInputStream ());
s2 = new ReadStream("stderr", p.getErrorStream ());
s1.start ();
s2.start ();
p.waitFor();        
} catch (Exception e) {  
e.printStackTrace();  
} finally {
    if(p != null)
        p.destroy();
}

【讨论】:

  • 是的,那段代码也让我免于无知呵呵:)
  • +1 是完美的例子,/C 是我们应该传递的重要参数。如果不传递此参数,代码将无法运行。 cmd 参数的 URL:docs.microsoft.com/en-us/windows-server/administration/…
  • 我想使用它,但是,我需要从ReadStream 返回一个值,而不是仅仅将流转储到System.out。我知道这可以用Callable 而不是Runnable 来完成,但它有点复杂。有没有我可以效仿的实际例子?最好遵循这些示例中所示的相同类型的结构。
【解决方案2】:

此代码基于相同的想法 Arham 的答案,但使用 java 8 并行流实现,这使其更加简洁。

public static String getOutputFromProgram(String program) throws IOException {
    Process proc = Runtime.getRuntime().exec(program);
    return Stream.of(proc.getErrorStream(), proc.getInputStream()).parallel().map((InputStream isForOutput) -> {
        StringBuilder output = new StringBuilder();
        try (BufferedReader br = new BufferedReader(new InputStreamReader(isForOutput))) {
            String line;
            while ((line = br.readLine()) != null) {
                output.append(line);
                output.append("\n");
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return output;
    }).collect(Collectors.joining());
}

你可以这样调用方法

getOutputFromProgram("cmd /C si viewhistory --fields=revision --project="+fileName);

请注意,如果您正在调用的程序挂起,此方法将挂起,如果需要输入,则会发生这种情况。

【讨论】:

  • 在运行 mvn verify 时效果很好,而且我没有用 cmd /C 包裹它——你知道包裹第二个 shell 有什么好处吗?
  • 我完全复制了 OP 的命令字符串,它以“cmd /C”开头。我认为在这种情况下包装它没有任何好处。
  • 这是一个很棒的代码块,可以按原样工作,但我注意到了一个问题。虽然这可以防止在一个曾经为我挂起的进程中挂起,但在另一个非常快的进程中,该进程创建了供其他事物快速使用的文件,但在尝试访问之前,这些文件的写入速度不够快。我以为 proc.waitfor 会修复它,但到目前为止还没有运气,您可能需要编辑代码块来解决在没有足够等待时可能出现的问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-07-04
  • 2012-10-14
  • 1970-01-01
  • 2011-01-09
  • 2015-08-28
  • 2012-06-21
相关资源
最近更新 更多