Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
process.waitFor();
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!)
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();
É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();
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!
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();
Il y a plusieurs possibilités:
stdout
du processus.stderr
du processus.stdin
du processus.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();
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");
}
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.
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();
}