web-dev-qa-db-fra.com

process.waitFor () ne retourne jamais

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

Il y a plusieurs raisons pour lesquelles waitFor() ne revienne pas.

Mais cela revient généralement au fait que la commande exécutée ne s’arrête pas.

Ceci, encore une fois, peut avoir plusieurs raisons.

Une des raisons courantes est que le processus produit une sortie et que vous ne lisez pas à partir des flux appropriés. Cela signifie que le processus est bloqué dès que le tampon est plein et attend que votre processus continue à lire. Votre processus attend à son tour que l'autre processus se termine (ce qui ne le sera pas car il attend votre processus, ...). C'est une situation d'impasse classique.

Vous devez lire en permanence à partir du flux d'entrée des processus pour vous assurer qu'il ne bloque pas.

Il y a un article de Nice qui explique tous les pièges de Runtime.exec() et montre comment les contourner s'appelle "Quand Runtime.exec () ne le fera pas" (oui, l'article date de 2000, mais le contenu s'applique toujours!)

124
Joachim Sauer

Il semble que vous ne lisiez pas le résultat avant d’attendre sa fin. Cela ne pose un problème que si la sortie ne remplit pas le tampon. Si tel est le cas, il attendra de lire le résultat, catch-22.

Peut-être que vous avez des erreurs que vous ne lisez pas. Dans ce cas, l’application s’arrêterait et attendrait pour toujours. Une solution simple consiste à rediriger les erreurs vers la sortie normale.

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

Également de Java doc:

Java.lang 

Processus de classe

Certaines plates-formes natives n'offrant qu'une taille de mémoire tampon limitée pour les entrées standard et flux de sortie, échec d'écriture rapide du flux d'entrée ou de lecture du flux de sortie de le sous-processus peut provoquer son blocage, voire son blocage.

Échec d'effacement du tampon du flux d'entrée (qui conduit au flux de sortie du sous-processus). from Process peut conduire à un blocage de sous-processus.

Essaye ça:

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

J'aimerais ajouter quelque chose aux réponses précédentes, mais comme je n'ai pas le représentant à commenter, je vais simplement ajouter une réponse. Cela s’adresse aux utilisateurs d’Android programmant en Java.

D'après le message de RollingBoy, ce code a presque fonctionné pour moi:

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

Dans mon cas, waitFor () ne libérait pas car j'exécutais une instruction sans retour ("ip adddr flush eth0"). Un moyen facile de résoudre ce problème consiste simplement à vous assurer de toujours renvoyer quelque chose dans votre déclaration. Pour moi, cela signifiait exécuter ce qui suit: "ip adddr flush eth0 && echo done". Vous pouvez lire le tampon toute la journée, mais si rien n'est jamais retourné, votre thread ne publiera jamais son attente.

J'espère que ça aide quelqu'un!

9
Slopes

Comme d'autres l'ont mentionné, vous devez utiliser stderr et stdout.

Comparé aux autres réponses, depuis Java 1.7, c'est encore plus facile. Vous n'avez plus besoin de créer vous-même des discussions pour lire stderr et stdout.

Utilisez simplement le ProcessBuilder et les méthodes redirectOutput en combinaison avec redirectError ou 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

Il y a plusieurs possibilités:

  1. Vous n'avez pas utilisé toutes les sorties de la variable stdout du processus.
  2. Vous n'avez pas utilisé toutes les sorties de la variable stderr du processus.
  3. Le processus attend input de votre part et vous ne l'avez pas fourni ou vous n'avez pas fermé la variable stdin du processus.
  4. Le processus tourne dans une boucle dure.
2
user207421

Pour la même raison, vous pouvez également utiliser inheritIO() pour mapper une console Java avec une console d'application externe, par exemple:

ProcessBuilder pb = new ProcessBuilder(appPath, arguments);

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

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

Vous devriez essayer de consommer la sortie et l’erreur dans le même

    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

Je pense avoir observé un problème similaire: certains processus ont démarré, semblaient fonctionner avec succès mais ne se sont jamais terminés. La fonction waitFor () attendait depuis toujours, sauf si je tuais le processus dans le Gestionnaire des tâches.
Cependant, tout a bien fonctionné dans les cas où la longueur de la ligne de commande ne dépassait pas 127 caractères. Si les noms de fichier longs sont inévitables, vous pouvez utiliser des variables d'environnement, ce qui peut vous permettre de garder la chaîne de ligne de commande courte. Vous pouvez générer un fichier de commandes (à l’aide de FileWriter) dans lequel vous définissez vos variables d’environnement avant d’appeler le programme que vous voulez exécuter .. .. Le contenu d’un tel lot pourrait ressembler à ceci:

    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%

La dernière étape consiste à exécuter ce fichier de commandes à l'aide de Runtime.

0
Kauz_at_Vancouver

Voici une méthode qui fonctionne pour moi ..___. NOTE: il y a du code dans cette méthode qui peut ne pas s'appliquer à vous, essayez donc de l'ignorer. Par exemple, "logStandardOut (...), git-bash, etc.".

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