процесс.waitFor () никогда не возвращается

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

9 ответов


есть много причин, что waitFor() не возвращает.

но обычно это сводится к тому, что выполняемая команда не завершается.

Это, опять же, может иметь множество причин.

одна из распространенных причин заключается в том, что процесс производит некоторый вывод, и вы не читаете из соответствующих потоков. Это означает, что процесс блокируется, как только буфер заполнен и ожидает продолжения чтения процесса. Процесс, в свою очередь, ждет другой процесс до конца (чего не будет, потому что он ждет вашего процесса ...). Это классическая тупиковая ситуация.

вам нужно постоянно читать из входного потока процессов, чтобы убедиться, что он не блокируется.

есть хорошая статья, которая объясняет все подводные камни Runtime.exec() и показывает пути их называли " Во Время Выполнения.метод exec() не" (да, статья с 2000 года, но содержание по-прежнему применяется!)


похоже, вы не читаете вывод, прежде чем ждать его завершения. Это нормально, только если выходные данные не заполняют буфер. Если это так, он будет ждать, пока вы не прочитаете вывод, 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();

также из Java doc:

java.Лэнг!--2-->

Процесс Класс

потому что некоторые собственные платформы предоставляют только ограниченный размер буфера для стандартного ввода и выходные потоки, неспособность быстро записать входной поток или прочитать выходной поток подпроцесс может привести к блокировке подпроцесса и даже взаимоблокировке.

Не удается очистить буфер входного потока (который подключается к выходному потоку подпроцесса) из процесса может привести к блокировке подпроцесса.

попробуйте это:

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

Я хотел бы добавить что-то к предыдущим ответам, но поскольку у меня нет представителя для комментариев, я просто добавлю ответ. Это направлено на пользователей android, которые программируют на Java.

на пост от 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". Вы можете читать буфер весь день, но если ничего не возвращено, ваш поток никогда не выпустит свое ожидание.

надеюсь, что это поможет кому-то!


как упоминали другие, вы должны потреблять stderr и stdout.

по сравнению с другими ответами, так как Java 1.7 это еще проще. Вам больше не нужно создавать потоки самостоятельно, чтобы читать stderr и stdout.

просто использовать ProcessBuilder и использовать методы redirectOutput в сочетании с redirectError или redirectErrorStream.

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

есть несколько вариантов:

  1. вы не израсходовали все выходные данные процесса stdout.
  2. вы не израсходовали все выходные данные процесса stderr.
  3. процесс ждет вход от вас, и вы не предоставили его, или вы не закрыли процесс stdin.
  4. процесс вращается в жесткой петле.

по той же причине вы также можете использовать inheritIO() сопоставить консоль Java с внешней консолью приложений, например:

ProcessBuilder pb = new ProcessBuilder(appPath, arguments);

pb.directory(new File(appFile.getParent()));
pb.inheritIO();

Process process = pb.start();
int success = process.waitFor();

Я думаю, что наблюдал аналогичную проблему: некоторые процессы запускались, казалось, успешно, но никогда не завершались. Функция 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%

последнем шаге выполняется этот пакетный файл с помощью среды выполнения.


вот метод, который работает для меня. Примечание: есть некоторые код в этот метод, могут не применяться к вам, поэтому постарайтесь игнорировать его. Например, "logStandardOut(...), ГИТ-Баш и т. д.".

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

}