processus.waitFor() ne retourne jamais

Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader = 
    new BufferedReader(new InputStreamReader(process.getInputStream()));
process.waitFor();
75
demandé sur default locale 2011-03-30 12:26:16

9 réponses

il y a plusieurs raisons pour lesquelles waitFor() ne revient pas.

mais il se résume habituellement au fait que la commande exécutée ne démissionne pas.

cela, encore une fois, peut avoir plusieurs raisons.

une raison courante est que le processus produit une certaine sortie et vous ne lisez pas à partir des flux appropriés. Cela signifie que le processus est bloqué dès que la mémoire est pleine et attend votre processus pour continuer la lecture. Votre processus à son tour attend l'autre processus pour finir (ce qui ne sera pas parce qu'il attend votre processus, ...). C'est un classique de la situation de blocage.

vous devez continuellement lire dans le flux d'entrée des processus pour vous assurer qu'il ne bloque pas.

il y a un bel article qui explique tous les pièges de Runtime.exec() et montre comment les contourner appelé " quand Runtime.exec() ne sont pas" (oui, l'article est à partir de 2000, mais le contenu s'applique toujours!)

121
répondu Joachim Sauer 2011-03-30 08:31:58

il semble que vous ne lisez pas la sortie avant d'attendre qu'elle se termine. C'est très bien seulement si la sortie ne remplit pas le tampon. Si c'est le cas, il attendra que vous lisiez la sortie, catch-22.

peut-être avez-vous des erreurs que vous ne lisez pas. Cela permettrait à l'application d'arrêter et d'attendre d'attendre pour toujours. Un moyen simple d'éviter cela est de rediriger les erreurs vers la sortie régulière.

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();
49
répondu Peter Lawrey 2011-03-30 08:38:19

aussi de Java doc:

de java.lang

Classe "Processus De 151920920"

parce que certaines plateformes natives ne fournissent qu'une taille de tampon limitée pour l'entrée standard et flux de sortie, défaut d'écrire rapidement le flux d'entrée ou de lire le flux de sortie de le sous-processus peut causer le blocage du sous-processus, et même l'impasse.

ne nettoie pas le tampon du flux d'entrée (qui conduit à le flux de sortie du sous-processus) du processus peut conduire à un blocage de sous-processus.

essayez ceci:

Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();
32
répondu RollingBoy 2013-08-02 06:44:20

j'aimerais ajouter quelque chose aux réponses précédentes mais comme je n'ai pas le représentant pour commenter, je vais juste ajouter une réponse. Ceci est dirigé vers les utilisateurs android qui programment en Java.

pour le post 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, le waitFor() ne lâchait pas parce que j'exécutais une instruction sans retour ("ip adddr flush eth0"). Un moyen facile de corriger cela est de simplement assurez-vous de toujours retourner quelque chose dans votre déclaration. Pour moi, cela signifiait que l'exécution de la suivante: "ip adddr rincer eth0 && echo". Vous pouvez lire le tampon toute la journée, mais s'il n'y a jamais rien retourné, votre fil ne relâchera jamais son attente.

Espère que ça aide quelqu'un!

8
répondu Slopes 2015-05-20 20:20:32

comme d'autres l'ont mentionné, vous devez consommer stderr et stdout .

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

il suffit D'utiliser les ProcessBuilder et d'utiliser 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();
3
répondu Markus Weninger 2017-10-07 03:25:37

il y a plusieurs possibilités:

  1. Vous n'avez pas consommé tous les résultats sur le processus de stdout .
  2. vous n'avez pas consommé toute la production sur le processus stderr .
  3. le processus attend entrée de votre part et vous ne l'avez pas fourni, ou vous n'avez pas fermé le processus stdin .
  4. le processus tourne en boucle.
2
répondu user207421 2016-04-20 10:21:11

pour la même raison, vous pouvez également utiliser inheritIO() pour cartographier Java console avec app console externe comme:

ProcessBuilder pb = new ProcessBuilder(appPath, arguments);

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

Process process = pb.start();
int success = process.waitFor();
1
répondu Vivek 2016-04-20 10:13:18

je pense que j'ai observé un problème similaire: certains processus ont commencé, ont semblé fonctionner avec succès mais jamais terminé. La fonction waitFor () attendait pour toujours sauf si j'ai tué le processus dans le Gestionnaire des tâches.

Cependant, tout fonctionnait bien dans les cas où la longueur de la ligne de commande était de 127 caractères ou moins. Si les noms de fichiers longs sont inévitables, vous pouvez utiliser des variables d'environnement, ce qui peut vous permettre de garder la chaîne de commande courte. Vous pouvez générer un fichier batch (en utilisant FileWriter) dans lequel vous définissez vos variables environnementales avant d'appeler le programme que vous voulez réellement lancer. Le contenu d'un tel lot pourrait ressembler à:

    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 est d'exécuter ce fichier batch en utilisant Runtime.

0
répondu Kauz_at_Vancouver 2011-11-28 01:46:51

Voici une méthode qui fonctionne pour moi. NOTE: Il y a un code dans cette méthode qui peut ne pas s'appliquer à vous, alors essayez 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
  // /q/process-waitfor-never-returns-31190/"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
répondu Sagan 2018-09-06 17:30:16