it-swarm.com.ru

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

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

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

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

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

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

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

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

124
Joachim Sauer

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

Также из документа Java:

Java.lang 

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

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

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

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

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

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

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

9
Slopes

Как уже упоминали другие, вы должны использовать 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();
4
Markus Weninger

Есть несколько возможностей:

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

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

ProcessBuilder pb = new ProcessBuilder(appPath, arguments);

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

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

Вы должны попробовать использовать вывод и ошибку в то же время

    private void runCMD(String CMD) throws IOException, InterruptedException {
    System.out.println("Standard output: " + CMD);
    Process process = Runtime.getRuntime().exec(CMD);

    // Get input streams
    BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream()));
    BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));
    String line = "";
    String newLineCharacter = System.getProperty("line.separator");

    boolean isOutReady = false;
    boolean isErrorReady = false;
    boolean isProcessAlive = false;

    boolean isErrorOut = true;
    boolean isErrorError = true;


    System.out.println("Read command ");
    while (process.isAlive()) {
        //Read the stdOut

        do {
            isOutReady = stdInput.ready();
            //System.out.println("OUT READY " + isOutReady);
            isErrorOut = true;
            isErrorError = true;

            if (isOutReady) {
                line = stdInput.readLine();
                isErrorOut = false;
                System.out.println("=====================================================================================" + line + newLineCharacter);
            }
            isErrorReady = stdError.ready();
            //System.out.println("ERROR READY " + isErrorReady);
            if (isErrorReady) {
                line = stdError.readLine();
                isErrorError = false;
                System.out.println("ERROR::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::" + line + newLineCharacter);

            }
            isProcessAlive = process.isAlive();
            //System.out.println("Process Alive " + isProcessAlive);
            if (!isProcessAlive) {
                System.out.println(":::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: Process DIE " + line + newLineCharacter);
                line = null;
                isErrorError = false;
                process.waitFor(1000, TimeUnit.MILLISECONDS);
            }

        } while (line != null);

        //Nothing else to read, lets pause for a bit before trying again
        System.out.println("PROCESS WAIT FOR");
        process.waitFor(100, TimeUnit.MILLISECONDS);
    }
    System.out.println("Command finished");
}
0
Eduardo Reyes

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

Последний шаг - запуск этого пакетного файла с использованием среды выполнения.

0
Kauz_at_Vancouver

Вот метод, который работает для меня . ПРИМЕЧАНИЕ. В этом методе есть некоторый код, который может к вам не относиться, поэтому попробуйте его игнорировать. Например, «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();

}

0
Sagan