Iniciando um processo com stdin/stdout/stderr herdado em Java 6
Pergunta
Se eu iniciar um processo via Java Construtor de Processos classe, tenho acesso total aos fluxos de entrada, saída padrão e erro padrão desse processo como Java InputStreams
e OutputStreams
.No entanto, não consigo encontrar uma maneira de conectar perfeitamente esses fluxos a System.in
, System.out
, e System.err
.
É possível usar redirectErrorStream()
para obter um único InputStream
que contém a saída padrão e o erro padrão do subprocesso, e apenas percorrer isso e enviá-lo através da minha saída padrão - mas não consigo encontrar uma maneira de fazer isso e deixar o usuário digitar no processo, como ele ou ela poderia se eu usei o C system()
chamar.
Isso parece ser possível no Java SE 7 quando for lançado - só estou me perguntando se há uma solução alternativa agora.Pontos de bônus se o resultado de isatty()
no processo filho realiza o redirecionamento.
Solução
Você precisará copiar o Processo fluxos out, err e input para as versões do sistema.A maneira mais fácil de fazer isso é usar o IOUtils classe do pacote Commons IO.O método de cópia parece ser o que você precisa.As invocações do método copy precisarão estar em threads separadas.
Aqui está o código básico:
// Assume you already have a processBuilder all configured and ready to go
final Process process = processBuilder.start();
new Thread(new Runnable() {public void run() {
IOUtils.copy(process.getOutputStream(), System.out);
} } ).start();
new Thread(new Runnable() {public void run() {
IOUtils.copy(process.getErrorStream(), System.err);
} } ).start();
new Thread(new Runnable() {public void run() {
IOUtils.copy(System.in, process.getInputStream());
} } ).start();
Outras dicas
Uma variação da resposta de John que compila e não exige que você use o Commons IO:
private static void pipeOutput(Process process) {
pipe(process.getErrorStream(), System.err);
pipe(process.getInputStream(), System.out);
}
private static void pipe(final InputStream src, final PrintStream dest) {
new Thread(new Runnable() {
public void run() {
try {
byte[] buffer = new byte[1024];
for (int n = 0; n != -1; n = src.read(buffer)) {
dest.write(buffer, 0, n);
}
} catch (IOException e) { // just exit
}
}
}).start();
}
Para System.in
use o seguinte pipein()
em vez de pipe()
pipein(System.in, p.getOutputStream());
Implementação:
private static void pipein(final InputStream src, final OutputStream dest) {
new Thread(new Runnable() {
public void run() {
try {
int ret = -1;
while ((ret = System.in.read()) != -1) {
dest.write(ret);
dest.flush();
}
} catch (IOException e) { // just exit
}
}
}).start();
}