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


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

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.Лэнг

Класс Process

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

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

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

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() не выпускать, потому что я выполнял с заявлением, с которой нет возврата ("ИС adddr заподлицо интерфейс 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. процесс ждет input от вас, и вы не предоставили его, или вы не закрыли процесс 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(...), 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();

}