Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
process.waitFor();
fuente
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
process.waitFor();
Hay muchas razones por las que waitFor()
no vuelve.
Pero generalmente se reduce al hecho de que el comando ejecutado no se cierra.
Esto, nuevamente, puede tener muchas razones.
Una razón común es que el proceso produce algunos resultados y no lee las secuencias adecuadas. Esto significa que el proceso se bloquea tan pronto como el búfer esté lleno y espera a que su proceso continúe leyendo. Su proceso, a su vez, espera a que termine el otro proceso (que no lo hará porque espera su proceso, ...). Ésta es una situación clásica de estancamiento.
Debe leer continuamente el flujo de entrada de los procesos para asegurarse de que no se bloquee.
Hay un buen artículo que explica todas las trampas Runtime.exec()
y muestra formas de evitarlas llamado "Cuando Runtime.exec () no lo hará" (sí, el artículo es de 2000, ¡pero el contenido aún se aplica!)
waitFor()
no regresa.
Parece que no está leyendo el resultado antes de esperar a que termine. Esto está bien solo si la salida no llena el búfer. Si lo hace, esperará hasta que lea el resultado, catch-22.
Quizás tenga algunos errores que no está leyendo. Esto haría que la aplicación se detenga y espere a que espere eternamente. Una forma sencilla de evitar esto es redirigir los errores a la salida normal.
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
solo está disponible desde Java 1.7
También de Java doc:
java.lang
Proceso de clase
Debido a que algunas plataformas nativas solo proporcionan un tamaño de búfer limitado para flujos de entrada y salida estándar, si no se escribe rápidamente el flujo de entrada o se lee el flujo de salida del subproceso, el subproceso se puede bloquear e incluso bloquear.
Si no se borra el búfer del flujo de entrada (que conduce al flujo de salida del subproceso) del proceso, se puede producir un bloqueo del subproceso.
Prueba esto:
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);
donde stoutOS y stderrOS son BufferedOutputStream
los que he creado para escribir en los archivos apropiados.
process.close()
. Pero cuando abro el flujo de entrada como se sugirió anteriormente y lo cierro inmediatamente, el problema desaparece. Entonces, en mi caso, Spring estaba esperando la señal de cierre de flujo. Aunque estoy usando Java 8 que se puede cerrar automáticamente.
Me gustaría agregar algo a las respuestas anteriores, pero como no tengo el representante para comentar, solo agregaré una respuesta. Esto está dirigido a usuarios de Android que están programando en Java.
Según la publicación de RollingBoy, este código casi funcionó para mí:
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();
En mi caso, waitFor () no se estaba publicando porque estaba ejecutando una declaración sin retorno ("ip adddr flush eth0"). Una forma sencilla de solucionar este problema es simplemente asegurarse de que siempre devuelve algo en su estado de cuenta. Para mí, eso significó ejecutar lo siguiente: "ip adddr flush eth0 && echo done". Puede leer el búfer todo el día, pero si no se devuelve nada, su hilo nunca liberará su espera.
¡Espero que ayude a alguien!
process.waitFor()
que se cuelga, es el reader.readLine()
que se cuelga si no tienes salida. Intenté usar el waitFor(long,TimeUnit)
tiempo de espera hasta si algo sale mal y descubrí que era la lectura el colgado. Lo que hace que la versión temporizada requiera otro hilo para hacer la lectura ...
Hay varias posibilidades:
stdout
.stderr
.stdin
.Como han mencionado otros, debes consumir stderr y stdout .
En comparación con las otras respuestas, desde Java 1.7 es aún más fácil. Ya no tiene que crear subprocesos usted mismo para leer stderr y stdout .
Simplemente use los ProcessBuilder
métodos y use redirectOutput
en combinación con redirectError
o 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();
Por la misma razón, también puede usar inheritIO()
para mapear la consola Java con la consola de la aplicación externa como:
ProcessBuilder pb = new ProcessBuilder(appPath, arguments);
pb.directory(new File(appFile.getParent()));
pb.inheritIO();
Process process = pb.start();
int success = process.waitFor();
Debería intentar consumir salida y error al mismo tiempo
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");
}
Creo que observé un problema similar: algunos procesos se iniciaron, parecían ejecutarse correctamente pero nunca se completaron. La función waitFor () estaba esperando para siempre, excepto si maté el proceso en el Administrador de tareas.
Sin embargo, todo funcionó bien en los casos en que la longitud de la línea de comando fuera de 127 caracteres o menos. Si los nombres de archivo largos son inevitables, es posible que desee utilizar variables ambientales, lo que puede permitirle mantener corta la cadena de línea de comandos. Puede generar un archivo por lotes (usando FileWriter) en el que establece sus variables ambientales antes de llamar al programa que realmente desea ejecutar. El contenido de dicho lote podría verse así:
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%
El último paso es ejecutar este archivo por lotes utilizando Runtime.
Aquí hay un método que me funciona. NOTA: Hay algún código dentro de este método que puede no aplicarse a usted, así que intente ignorarlo. Por ejemplo, "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 lectura asincrónica de la secuencia combinada con evitar la espera con un tiempo de espera resolverá el problema.
Puede encontrar una página que explica esto aquí http://simplebasics.net/.net/process-waitforexit-with-a-timeout-will-not-be-able-to-collect-the-output-message/