Estou construindo um processo em Java usando ProcessBuilder da seguinte maneira:
ProcessBuilder pb = new ProcessBuilder()
.command("somecommand", "arg1", "arg2")
.redirectErrorStream(true);
Process p = pb.start();
InputStream stdOut = p.getInputStream();
Agora, meu problema é o seguinte: Eu gostaria de capturar tudo o que está passando por stdout e / ou stderr desse processo e redirecioná-lo de System.out
forma assíncrona. Quero que o processo e seu redirecionamento de saída sejam executados em segundo plano. Até agora, a única maneira que encontrei de fazer isso é gerar manualmente um novo thread que lerá continuamente stdOut
e chamará o write()
método apropriado de System.out
.
new Thread(new Runnable(){
public void run(){
byte[] buffer = new byte[8192];
int len = -1;
while((len = stdOut.read(buffer)) > 0){
System.out.write(buffer, 0, len);
}
}
}).start();
Embora essa abordagem funcione, parece um pouco suja. E, além disso, me dá mais um thread para gerenciar e encerrar corretamente. Existe alguma maneira melhor de fazer isso?
java
processbuilder
LordOfThePigs
fonte
fonte
org.apache.commons.io.IOUtils.copy(new ProcessBuilder().command(commandLine) .redirectErrorStream(true).start().getInputStream(), System.out);
Respostas:
A única maneira no Java 6 ou anterior é com um assim chamado
StreamGobbler
(que você começou a criar):...
Para Java 7, consulte a resposta de Evgeniy Dorofeev.
fonte
Use
ProcessBuilder.inheritIO
, ele define a origem e o destino para E / S padrão de subprocesso como sendo os mesmos do processo Java atual.Se Java 7 não for uma opção
Threads morrerão automaticamente quando o subprocesso terminar, porque
src
EOF.fonte
inheritIO()
um dessesredirect*(ProcessBuilder.Redirect)
métodos úteis na próxima vez que precisar fazer isso em um projeto java 7. Infelizmente, meu projeto é java 6.sc
precisa ser fechado?Uma solução flexível com lambda Java 8 que permite fornecer um
Consumer
que processará a saída (por exemplo, registrá-la) linha por linha.run()
é um one-liner sem exceções verificadas lançadas. Como alternativa à implementaçãoRunnable
, ele pode se estender,Thread
como outras respostas sugerem.Você pode usá-lo, por exemplo, desta forma:
Aqui, o fluxo de saída é redirecionado para
System.out
e o fluxo de erro é registrado no nível de erro pelologger
.fonte
forEach()
norun()
método irá bloquear até que o fluxo seja aberto, aguardando a próxima linha. Ele sairá quando o fluxo for fechado.É tão simples quanto seguir:
por .redirectErrorStream (true) você diz ao processo para mesclar erro e fluxo de saída e, em seguida, por .redirectOutput (arquivo), você redireciona a saída mesclada para um arquivo.
Atualizar:
Eu consegui fazer isso da seguinte maneira:
Agora você pode ver as duas saídas dos threads principal e asyncOut em System.out
fonte
Solução java8 simples com captura de saídas e processamento reativo usando
CompletableFuture
:E o uso:
fonte
Existe uma biblioteca que fornece um ProcessBuilder melhor, zt-exec. Esta biblioteca pode fazer exatamente o que você está pedindo e muito mais.
Aqui está como seu código ficaria com zt-exec em vez de ProcessBuilder:
adicione a dependência:
O código :
A documentação da biblioteca está aqui: https://github.com/zeroturnaround/zt-exec/
fonte
Eu também posso usar apenas Java 6. Usei a implementação do scanner de thread de @EvgeniyDorofeev. No meu código, depois que um processo termina, tenho que executar imediatamente dois outros processos, cada um comparando a saída redirecionada (um teste de unidade baseado em diff para garantir que stdout e stderr são iguais aos abençoados).
Os threads do scanner não terminam logo o suficiente, mesmo se eu esperar () que o processo seja concluído. Para fazer o código funcionar corretamente, tenho que garantir que os threads sejam unidos após o término do processo.
fonte
Como complemento à resposta do msangel , gostaria de adicionar o seguinte bloco de código:
Ele permite redirecionar o fluxo de entrada (stdout, stderr) do processo para algum outro consumidor. Isso pode ser System.out :: println ou qualquer outra coisa que consuma strings.
Uso:
fonte
Seu código personalizado vai em vez do
...
fonte
Por padrão, o subprocesso criado não possui seu próprio terminal ou console. Todas as suas operações de E / S padrão (isto é, stdin, stdout, stderr) serão redirecionadas para o processo pai, onde podem ser acessadas através dos fluxos obtidos usando os métodos getOutputStream (), getInputStream () e getErrorStream (). O processo pai usa esses fluxos para alimentar a entrada e obter a saída do subprocesso. Como algumas plataformas nativas fornecem apenas um tamanho de buffer limitado para fluxos de entrada e saída padrão, a falha em gravar prontamente o fluxo de entrada ou ler o fluxo de saída do subprocesso pode causar o bloqueio do subprocesso ou até mesmo um impasse.
https://www.securecoding.cert.org/confluence/display/java/FIO07-J.+Do+not+let+external+processes+block+on+IO+buffers
fonte