Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
process.waitFor();
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
process.waitFor();
Réponses:
Il y a de nombreuses raisons qui waitFor()
ne reviennent pas.
Mais cela se résume généralement au fait que la commande exécutée ne se ferme pas.
Ceci, encore une fois, peut avoir de nombreuses raisons.
Une raison courante est que le processus produit une sortie et que vous ne lisez pas les flux appropriés. Cela signifie que le processus est bloqué dès que la mémoire tampon est pleine et attend que votre processus continue sa lecture. Votre processus attend à son tour que l'autre processus se termine (ce qu'il ne fera pas car il attend votre processus, ...). C'est une situation classique de blocage.
Vous devez continuellement lire le flux d'entrée des processus pour vous assurer qu'il ne se bloque pas.
Il y a un bel article qui explique tous les pièges Runtime.exec()
et montre des moyens de les contourner appelé "Quand Runtime.exec () ne le fera pas" (oui, l'article date de 2000, mais le contenu s'applique toujours!)
waitFor()
ne revient pas.
Il semble que vous ne lisez pas la sortie avant d'attendre qu'elle se termine. Cela ne fonctionne que 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 de s'arrêter et d'attendre pour attendre indéfiniment. Un moyen simple de contourner ce problème 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();
pb.redirectError(new File("/dev/null"));
redirectError
n'est disponible que depuis Java 1.7
Également de Java doc:
java.lang
Processus de classe
Étant donné que certaines plates-formes natives ne fournissent qu'une taille de tampon limitée pour les flux d'entrée et de sortie standard, l'échec de l'écriture rapide du flux d'entrée ou de la lecture du flux de sortie du sous-processus peut entraîner le blocage du sous-processus, voire un blocage.
L'échec de l'effacement du tampon du flux d'entrée (qui conduit vers le flux de sortie du sous-processus) de Process peut entraîner un blocage du sous-processus.
Essaye ça:
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();
DefaultExecutor executor = new DefaultExecutor(); PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(stdoutOS, stderrOS); executor.setStreamHandler(pumpStreamHandler); executor.execute(cmdLine);
:, où stoutOS et stderrOS sont BufferedOutputStream
s que j'ai créés pour écrire dans les fichiers appropriés.
process.close()
. Mais lorsque j'ouvre le flux d'entrée comme suggéré ci-dessus et que je ferme immédiatement, le problème disparaît. Donc, dans mon cas, Spring attendait le signal de fermeture du flux. Même si j'utilise Java 8 fermable automatiquement.
Je voudrais 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. Ceci est destiné aux utilisateurs d'Android qui programment en Java.
Selon 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 se libérait pas car j'exécutais une instruction sans retour ("ip adddr flush eth0"). Un moyen simple de résoudre ce problème consiste simplement à vous assurer de toujours renvoyer quelque chose dans votre relevé. 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 relâchera jamais son attente.
J'espère que cela aide quelqu'un!
process.waitFor()
qui se bloque, c'est le reader.readLine()
qui se bloque si vous n'avez pas de sortie. J'ai essayé d'utiliser le waitFor(long,TimeUnit)
délai d'expiration en cas de problème et j'ai découvert que c'était la lecture du blocage. Ce qui rend la version expirée nécessite un autre thread pour faire la lecture ...
Il existe plusieurs possibilités:
stdout
.stderr
.stdin
.Comme d'autres l'ont mentionné, vous devez consommer stderr et stdout .
Par rapport 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 threads pour lire stderr et stdout .
Utilisez simplement le ProcessBuilder
et utilisez 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();
Pour la même raison, vous pouvez également utiliser inheritIO()
pour mapper la console Java avec une console d'application externe comme:
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 en même temps
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 correctement mais ne se sont jamais terminés. 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 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 souhaitez réellement exécuter. 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 consiste à exécuter ce fichier de commandes à l'aide de Runtime.
Voici une méthode qui fonctionne pour moi. REMARQUE: il existe 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
// /programming/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();
}
La lecture asynchrone du flux combinée à éviter Attendre avec un délai d'expiration résoudra le problème.
Vous pouvez trouver une page expliquant cela ici http://simplebasics.net/.net/process-waitforexit-with-a-timeout-will-not-be-able-to-collect-the-output-message/