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();
Respostas:
Existem muitos motivos que waitFor()
não voltam.
Mas geralmente se resume ao fato de que o comando executado não é encerrado.
Isso, novamente, pode ter muitas razões.
Um motivo comum é que o processo produz alguma saída e você não lê os fluxos apropriados. Isso significa que o processo é bloqueado assim que o buffer está cheio e espera que seu processo continue lendo. O seu processo, por sua vez, espera que o outro processo termine (o que não acontecerá porque espera pelo seu processo, ...). Esta é uma situação de impasse clássica.
Você precisa ler continuamente o fluxo de entrada dos processos para garantir que ele não bloqueie.
Há um bom artigo que explica todas as armadilhas Runtime.exec()
e mostra maneiras de contorná-las, chamado "Quando Runtime.exec () não vai" (sim, o artigo é de 2000, mas o conteúdo ainda se aplica!)
waitFor()
que não retorna.
Parece que você não está lendo a saída antes de aguardar sua conclusão. Isso só funciona se a saída não preencher o buffer. Em caso afirmativo, ele esperará até que você leia a saída, catch-22.
Talvez você tenha alguns erros que não está lendo. Isso faria com que o aplicativo parasse e esperasse para sempre. Uma maneira simples de contornar isso é redirecionar os erros para a saída regular.
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
está disponível apenas a partir do Java 1.7
Também do documento Java:
java.lang
Processo de aula
Como algumas plataformas nativas fornecem apenas um tamanho de buffer limitado para fluxos de entrada e saída padrão, a falha em escrever prontamente o fluxo de entrada ou ler o fluxo de saída do subprocesso pode causar o bloqueio do subprocesso e até mesmo um impasse.
Falha ao limpar o buffer do fluxo de entrada (que canaliza para o fluxo de saída do subprocesso) do Processo pode levar a um bloqueio do subprocesso.
Experimente isto:
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);
:, onde stoutOS e stderrOS são BufferedOutputStream
s que criei para gravar nos arquivos apropriados.
process.close()
. Mas quando eu abro o fluxo de entrada conforme sugerido acima e fecho imediatamente - o problema desaparece. Portanto, no meu caso, a Spring estava esperando pelo sinal de fechamento do stream. Embora eu esteja usando o Java 8 que pode ser fechado automaticamente.
Gostaria de acrescentar algo às respostas anteriores, mas como não tenho o representante para comentar, apenas acrescentarei uma resposta. Isso é direcionado para usuários de Android que estão programando em Java.
De acordo com a postagem de RollingBoy, este código quase funcionou para mim:
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();
No meu caso, o waitFor () não estava liberando porque eu estava executando uma instrução sem retorno ("ip adddr flush eth0"). Uma maneira fácil de corrigir isso é simplesmente garantir que você sempre retorne algo em seu extrato. Para mim, isso significava executar o seguinte: "ip adddr flush eth0 && echo done". Você pode ler o buffer o dia todo, mas se nada for retornado, sua thread nunca irá liberar sua espera.
Espero que ajude alguém!
process.waitFor()
que trava, é o reader.readLine()
que trava se você não tiver saída. Tentei usar o waitFor(long,TimeUnit)
para timeout se algo desse errado e descobri que era o read que travava. O que faz com que a versão expirada exija outro thread para fazer a leitura ...
Existem várias possibilidades:
stdout
.stderr
.stdin
.Como outros mencionaram, você deve consumir stderr e stdout .
Em comparação com as outras respostas, desde o Java 1.7 é ainda mais fácil. Você não precisa mais criar threads para ler stderr e stdout .
Basta usar ProcessBuilder
e usar os métodos redirectOutput
em combinação com 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();
Pelo mesmo motivo, você também pode usar inheritIO()
para mapear o console Java com console de aplicativo externo como:
ProcessBuilder pb = new ProcessBuilder(appPath, arguments);
pb.directory(new File(appFile.getParent()));
pb.inheritIO();
Process process = pb.start();
int success = process.waitFor();
Você deve tentar consumir a saída e o erro ao mesmo tempo
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");
}
Acho que observei um problema semelhante: alguns processos foram iniciados, pareciam funcionar com sucesso, mas nunca foram concluídos. A função waitFor () estava esperando uma eternidade, exceto se eu encerrasse o processo no Gerenciador de Tarefas.
No entanto, tudo funcionou bem nos casos em que o comprimento da linha de comando era de 127 caracteres ou menos. Se nomes de arquivo longos forem inevitáveis, você pode querer usar variáveis ambientais, o que pode permitir que você mantenha a string da linha de comando curta. Você pode gerar um arquivo em lote (usando FileWriter) no qual define suas variáveis de ambiente antes de chamar o programa que realmente deseja executar. O conteúdo desse lote pode ser semelhante a:
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%
A última etapa é executar este arquivo em lote usando o Runtime.
Aqui está um método que funciona para mim. NOTA: Há algum código neste método que pode não se aplicar a você, portanto, tente ignorá-lo. Por exemplo, "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();
}
A leitura assíncrona do fluxo combinada com evitar a espera com um tempo limite resolverá o problema.
Você pode encontrar uma página explicando isso aqui http://simplebasics.net/.net/process-waitforexit-with-a-timeout-will-not-be-able-to-collect-the-output-message/