process.waitFor()永远不会返回

时间:2011-03-30 08:26:17

标签: java runtime.exec

Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader = 
    new BufferedReader(new InputStreamReader(process.getInputStream()));
process.waitFor();

11 个答案:

答案 0 :(得分:128)

waitFor()没有返回的原因有很多。

但它通常归结为执行的命令不会退出。

这又有很多原因。

一个常见原因是该过程会产生一些输出,而您无法从适当的流中读取。这意味着一旦缓冲区已满,该进程就会被阻止,并等待您的进程继续读取。你的进程反过来等待另一个进程完成(它不会因为它等待你的进程,...)。这是一个典型的僵局。

您需要不断读取进程输入流,以确保它不会阻塞。

有一篇很好的文章解释了Runtime.exec()的所有陷阱,并展示了围绕它们的方式"When Runtime.exec() won't"(是的,文章来自2000年,但内容仍适用!)

答案 1 :(得分:62)

在等待输出完成之前,您似乎没有读取输出。仅当输出未填充缓冲区时才可以。如果是,它将等到你读取输出,catch-22。

也许你有一些你不读的错误。这将使应用程序停止并等待永远等待。一个简单的方法是将错误重定向到常规输出。

ProcessBuilder pb = new ProcessBuilder("tasklist");
pb.redirectErrorStream(true);
Process process = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null)
    System.out.println("tasklist: " + line);
process.waitFor();

答案 2 :(得分:36)

同样来自Java doc:

  

java.lang

     

班级流程

     

因为某些本机平台仅为标准输入提供有限的缓冲区大小   输出流,无法及时写入输入流或读取输出流   子进程可能导致子进程阻塞,甚至死锁。

     

无法清除输入流的缓冲区(管道到子进程的输出流)   来自Process可能会导致子进程阻塞。

试试这个:

Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();

答案 3 :(得分:9)

我想在之前的答案中添加一些内容,但由于我没有代表评论,我只想添加一个答案。这是针对用Java编程的Android用户。

根据RollingBoy的帖子,这段代码几乎对我有用:

Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();

在我的情况下,waitFor()没有释放,因为我正在执行一个没有返回的语句(“ip adddr flush eth0”)。解决这个问题的一个简单方法就是确保始终在语句中返回一些内容。对我来说,这意味着执行以下操作:“ip adddr flush eth0&& echo done”。你可以整天读取缓冲区,但如果没有返回任何内容,你的线程将永远不会释放它的等待。

希望能帮助别人!

答案 4 :(得分:4)

正如其他人所说,你必须使用 stderr stdout

与其他答案相比,Java 1.7更加容易。您不必再自己创建线程来阅读 stderr stdout

只需使用ProcessBuilder,并将方法redirectOutputredirectErrorredirectErrorStream结合使用。

String directory = "/working/dir";
File out = new File(...); // File to write stdout to
File err = new File(...); // File to write stderr to
ProcessBuilder builder = new ProcessBuilder();
builder.directory(new File(directory));
builder.command(command);
builder.redirectOutput(out); // Redirect stdout to file
if(out == err) { 
  builder.redirectErrorStream(true); // Combine stderr into stdout
} else { 
  builder.redirectError(err); // Redirect stderr to file
}
Process process = builder.start();

答案 5 :(得分:2)

有几种可能性:

  1. 您还没有消耗流程stdout上的所有输出。
  2. 您还没有消耗流程stderr上的所有输出。
  3. 此过程正在等待您的输入,但您尚未提供输入,或者您尚未关闭该流程的stdin
  4. 这个过程在硬循环中旋转。

答案 6 :(得分:1)

出于同样的原因,您还可以使用ProcessBuilder pb = new ProcessBuilder(appPath, arguments); pb.directory(new File(appFile.getParent())); pb.inheritIO(); Process process = pb.start(); int success = process.waitFor(); 将Java控制台映射到外部应用控制台,如:

{{1}}

答案 7 :(得分:0)

我认为我发现了一个类似的问题:一些流程开始,似乎成功运行但从未完成。函数waitFor()永远等待,除非我在任务管理器中杀死了进程 但是,在命令行长度为127个字符或更短的情况下,一切都运行良好。如果长文件名是不可避免的,您可能希望使用环境变量,这可能允许您保持命令行字符串短。您可以在调用实际要运行的程序之前生成批处理文件(使用FileWriter),在该文件中设置环境变量。 这样一批的内容可能如下:

    set INPUTFILE="C:\Directory 0\Subdirectory 1\AnyFileName"
    set OUTPUTFILE="C:\Directory 2\Subdirectory 3\AnotherFileName"
    set MYPROG="C:\Directory 4\Subdirectory 5\ExecutableFileName.exe"
    %MYPROG% %INPUTFILE% %OUTPUTFILE%

最后一步是使用Runtime运行此批处理文件。

答案 8 :(得分:0)

这是一种对我有用的方法。 注意:此方法中有一些代码可能不适用于您,因此请忽略它。例如“ logStandardOut(...),git-bash等”。

private String exeShellCommand(String doCommand, String inDir, boolean ignoreErrors) {
logStandardOut("> %s", doCommand);

ProcessBuilder builder = new ProcessBuilder();
StringBuilder stdOut = new StringBuilder();
StringBuilder stdErr = new StringBuilder();

boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");
if (isWindows) {
  String gitBashPathForWindows = "C:\\Program Files\\Git\\bin\\bash";
  builder.command(gitBashPathForWindows, "-c", doCommand);
} else {
  builder.command("bash", "-c", doCommand);
}

//Do we need to change dirs?
if (inDir != null) {
  builder.directory(new File(inDir));
}

//Execute it
Process process = null;
BufferedReader brStdOut;
BufferedReader brStdErr;
try {
  //Start the command line process
  process = builder.start();

  //This hangs on a large file
  // https://stackoverflow.com/questions/5483830/process-waitfor-never-returns
  //exitCode = process.waitFor();

  //This will have both StdIn and StdErr
  brStdOut = new BufferedReader(new InputStreamReader(process.getInputStream()));
  brStdErr = new BufferedReader(new InputStreamReader(process.getErrorStream()));

  //Get the process output
  String line = null;
  String newLineCharacter = System.getProperty("line.separator");

  while (process.isAlive()) {
    //Read the stdOut
    while ((line = brStdOut.readLine()) != null) {
      stdOut.append(line + newLineCharacter);
    }

    //Read the stdErr
    while ((line = brStdErr.readLine()) != null) {
      stdErr.append(line + newLineCharacter);
    }

    //Nothing else to read, lets pause for a bit before trying again
    process.waitFor(100, TimeUnit.MILLISECONDS);
  }

  //Read anything left, after the process exited
  while ((line = brStdOut.readLine()) != null) {
    stdOut.append(line + newLineCharacter);
  }

  //Read anything left, after the process exited
  while ((line = brStdErr.readLine()) != null) {
    stdErr.append(line + newLineCharacter);
  }

  //cleanup
  if (brStdOut != null) {
    brStdOut.close();
  }

  if (brStdErr != null) {
    brStdOut.close();
  }

  //Log non-zero exit values
  if (!ignoreErrors && process.exitValue() != 0) {
    String exMsg = String.format("%s%nprocess.exitValue=%s", stdErr, process.exitValue());
    throw new ExecuteCommandException(exMsg);
  }

} catch (ExecuteCommandException e) {
  throw e;
} catch (Exception e) {
  throw new ExecuteCommandException(stdErr.toString(), e);
} finally {
  //Log the results
  logStandardOut(stdOut.toString());
  logStandardError(stdErr.toString());
}

return stdOut.toString();

}

答案 9 :(得分:0)

您应该同时尝试消耗输出和错误

def remove_spaces(s):
    s = s.join(['A', 'A'])
    return ''.join([c for i, c in enumerate(s)
                        if c!=' ' or not (s[i-1]+s[i+1]).islower()][1:-1])

答案 10 :(得分:0)

异步读取流并避免等待超时将解决此问题。

您可以在http://simplebasics.net/.net/process-waitforexit-with-a-timeout-will-not-be-able-to-collect-the-output-message/

上找到解释此内容的页面