it-swarm.com.de

process.waitFor () kehrt nie zurück

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

Es gibt viele Gründe, warum waitFor() nicht zurückkehrt.

Normalerweise läuft es darauf hinaus, dass der ausgeführte Befehl nicht beendet wird.

Dies kann wiederum viele Gründe haben.

Ein häufiger Grund ist, dass der Prozess etwas ausgibt und Sie nicht aus den entsprechenden Streams lesen. Dies bedeutet, dass der Prozess blockiert wird, sobald der Puffer voll ist und wartet, bis der Prozess weiter gelesen wird. Ihr Prozess wartet wiederum, bis der andere Prozess abgeschlossen ist (was jedoch nicht der Fall ist, da er auf Ihren Prozess wartet ...). Dies ist eine klassische Deadlock-Situation.

Sie müssen fortlaufend aus dem Eingangsstrom der Prozesse lesen, um sicherzustellen, dass er nicht blockiert.

Es gibt einen schönen Artikel, der alle Fallstricke von Runtime.exec() erläutert und die Umgehung von "When Runtime.exec ()" () nicht zeigt (ja, der Artikel stammt aus dem Jahr 2000, der Inhalt gilt jedoch noch!)

124
Joachim Sauer

Es scheint, dass Sie die Ausgabe nicht lesen, bevor Sie warten, bis sie beendet ist. Dies ist nur in Ordnung, wenn die Ausgabe den Puffer nicht füllt. Wenn dies der Fall ist, wird gewartet, bis Sie die Ausgabe catch-22 gelesen haben.

Vielleicht haben Sie einige Fehler, die Sie nicht lesen. Dies würde dazu führen, dass die Anwendung stoppt und waitFor für immer wartet. Eine einfache Möglichkeit, dies zu umgehen, besteht darin, die Fehler an die reguläre Ausgabe weiterzuleiten.

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

Auch aus Java-Dokument:

Java.lang 

Klassenprozess

Einige native Plattformen bieten nur eine begrenzte Puffergröße für die Standardeingabe und Ausgabeströme, Fehler beim sofortigen Schreiben des Eingabestroms oder Lesen des Ausgabestroms von Der Unterprozess kann dazu führen, dass der Unterprozess blockiert oder sogar blockiert wird.

Fehler beim Löschen des Puffers des Eingabestroms (der zum Ausgabestrom des Unterprozesses leitet) from Process kann zu einer Blockierung von Unterprozessen führen.

Versuche dies:

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

Ich möchte den vorherigen Antworten etwas hinzufügen, aber da ich keine Vertreterin habe, möchte ich nur eine Antwort hinzufügen. Dies richtet sich an Android-Benutzer, die in Java programmieren.

Per Post von RollingBoy hat dieser Code fast für mich funktioniert:

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

In meinem Fall wurde das waitFor () nicht freigegeben, da ich eine Anweisung ohne Rückgabe ("ip adddr flush eth0") ausführte. Sie können dies einfach beheben, indem Sie einfach sicherstellen, dass Sie immer etwas in Ihrer Anweisung zurückgeben. Für mich bedeutete dies Folgendes auszuführen: "ip adddr flush eth0 && echo done". Sie können den Puffer den ganzen Tag lesen, aber wenn nichts zurückgegeben wird, gibt Ihr Thread niemals seine Wartezeit frei.

Hoffe das hilft jemandem!

9
Slopes

Wie bereits erwähnt, müssen Sie stderr und stdout konsumieren.

Im Vergleich zu den anderen Antworten ist es seit Java 1.7 noch einfacher. Sie müssen keine Threads mehr erstellen, um stderr und stdout lesen zu können.

Verwenden Sie einfach die ProcessBuilder und verwenden Sie die Methoden redirectOutput in Kombination mit entweder redirectError oder 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

Es gibt mehrere Möglichkeiten:

  1. Sie haben nicht die gesamte Ausgabe der stdout des Prozesses verbraucht.
  2. Sie haben nicht die gesamte Ausgabe der stderr des Prozesses verbraucht.
  3. Der Prozess wartet auf input von Ihnen und Sie haben ihn nicht angegeben oder Sie haben die stdin des Prozesses nicht geschlossen.
  4. Der Prozess dreht sich in einer harten Schleife.
2
user207421

Aus dem gleichen Grund können Sie auch inheritIO() verwenden, um die Java-Konsole mit der externen App-Konsole zu verknüpfen.

ProcessBuilder pb = new ProcessBuilder(appPath, arguments);

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

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

Sie sollten versuchen, Ausgabe und Fehler gleichzeitig zu verbrauchen

    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

Ich glaube, ich habe ein ähnliches Problem festgestellt: Einige Prozesse wurden gestartet, schienen erfolgreich zu laufen, wurden jedoch nicht abgeschlossen. Die Funktion waitFor () hat immer gewartet, es sei denn, ich habe den Prozess im Task-Manager beendet.
Alles funktionierte gut, wenn die Länge der Befehlszeile 127 Zeichen oder weniger betrug. Wenn lange Dateinamen unvermeidlich sind, möchten Sie möglicherweise Umgebungsvariablen verwenden, die möglicherweise die Befehlszeilenzeichenfolge kurz halten. Sie können eine Batchdatei (mit FileWriter) generieren, in der Sie Ihre Umgebungsvariablen festlegen, bevor Sie das Programm aufrufen, das Sie tatsächlich ausführen möchten. Der Inhalt eines solchen Batches könnte folgendermaßen aussehen:

    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%

Im letzten Schritt wird diese Batchdatei mit Runtime ausgeführt.

0

Hier ist eine Methode, die für mich funktioniert .. HINWEIS: In dieser Methode ist Code, der möglicherweise nicht für Sie gilt. Zum Beispiel "logStandardOut (...), git-bash usw.".

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