Capturando stdout ao chamar Runtime.exec

votos
50

Quando enfrentando problemas em máquinas de cliente de rede, eu gostaria de ser capaz de executar algumas linhas de comando e enviar e-mail os resultados deles para mim.

Eu encontrei Runtime.exec me permite executar comandos arbitrários, mas coletar os resultados em uma String é mais interessante.

Eu percebo que eu poderia redirecionar a saída para um arquivo e, em seguida, ler a partir do arquivo, mas meu senso spidey está me dizendo que há uma maneira mais elegante de fazer isso.

Sugestões?

Publicado 19/05/2009 em 14:31
fonte usuário
Em outras línguas...                            


8 respostas

votos
46

Você precisa capturar tanto o std fora e err std no processo. Você pode então escrever std para um arquivo / mail ou similar.

Consulte este artigo para mais informações e, em particular, note que o StreamGobblermecanismo que captura stdout / err em segmentos separados. Isto é essencial para evitar o bloqueio e é a fonte de muitos erros se você não fazê-lo corretamente!

Respondeu 19/05/2009 em 14:35
fonte usuário

votos
13

Use ProcessBuilder . Depois de chamar start () você vai ter um processo objeto do qual você pode obter os fluxos stderr e stdout.

UPDATE: ProcessBuilder lhe dá mais controle; Você não tem que usá-lo, mas acho que é mais fácil a longo prazo. Especialmente a capacidade de redirecionar stderr para stdout que significa que você só tem que chupar baixo um riacho.

Respondeu 19/05/2009 em 14:33
fonte usuário

votos
5

Use plexo Utils , ele é usado pelo Maven para EXECUT todos os processos externos.

Commandline commandLine = new Commandline();
commandLine.setExecutable(executable.getAbsolutePath());

Collection<String> args = getArguments();

for (String arg : args) {
    Arg _arg = commandLine.createArg();
    _arg.setValue(arg);
}

WriterStreamConsumer systemOut = new WriterStreamConsumer(console);
WriterStreamConsumer systemErr = new WriterStreamConsumer(console);

returnCode = CommandLineUtils.executeCommandLine(commandLine, systemOut, systemErr, 10);
if (returnCode != 0) {
    // bad
} else {
    // good
}
Respondeu 19/05/2009 em 14:42
fonte usuário

votos
4

Para os processos que não geram muita saída, penso que esta solução simples que utiliza Apache IOUtils é suficiente:

Process p = Runtime.getRuntime().exec("script");
p.waitFor();
String output = IOUtils.toString(p.getInputStream());
String errorOutput = IOUtils.toString(p.getErrorStream());

Aviso: No entanto, se o seu processo gera muita saída, esta abordagem pode causar problemas, como mencionado no JavaDoc classe Process :

O subprocesso criado não tem o seu próprio terminal ou console. Todos os seus io padrão (isto é, entrada padrão, saída padrão, stderr) operações será redireccionado para o processo de origem através de três fluxos (getOutputStream (), getInputStream (), getErrorStream ()). O processo pai usa esses fluxos para alimentar a entrada e obter a saída do subprocesso. Porque algumas plataformas nativos só fornecem tamanho do buffer limitado para os fluxos de entrada e de saída padrão, a incapacidade de escrever prontamente o fluxo de entrada ou ler o fluxo do subprocesso de saída pode fazer com que o sub-processo para bloquear, e mesmo impasse.

Respondeu 24/06/2014 em 23:10
fonte usuário

votos
2

Esta é a minha classe auxiliar usado por anos. Uma classe pequena. Ele tem classe streamgobbler JavaWorld para corrigir vazamentos de recursos JVM. Não sei se ainda é válido para JVM6 e JVM7 mas não faz mal. Helper pode ler buffer de saída para uso posterior.

import java.io.*;

/**
 * Execute external process and optionally read output buffer.
 */
public class ShellExec {
    private int exitCode;
    private boolean readOutput, readError;
    private StreamGobbler errorGobbler, outputGobbler;

    public ShellExec() { 
        this(false, false);
    }

    public ShellExec(boolean readOutput, boolean readError) {
        this.readOutput = readOutput;
        this.readError = readError;
    }

    /**
     * Execute a command.
     * @param command   command ("c:/some/folder/script.bat" or "some/folder/script.sh")
     * @param workdir   working directory or NULL to use command folder
     * @param wait  wait for process to end
     * @param args  0..n command line arguments
     * @return  process exit code
     */
    public int execute(String command, String workdir, boolean wait, String...args) throws IOException {
        String[] cmdArr;
        if (args != null && args.length > 0) {
            cmdArr = new String[1+args.length];
            cmdArr[0] = command;
            System.arraycopy(args, 0, cmdArr, 1, args.length);
        } else {
            cmdArr = new String[] { command };
        }

        ProcessBuilder pb =  new ProcessBuilder(cmdArr);
        File workingDir = (workdir==null ? new File(command).getParentFile() : new File(workdir) );
        pb.directory(workingDir);

        Process process = pb.start();

        // Consume streams, older jvm's had a memory leak if streams were not read,
        // some other jvm+OS combinations may block unless streams are consumed.
        errorGobbler  = new StreamGobbler(process.getErrorStream(), readError);
        outputGobbler = new StreamGobbler(process.getInputStream(), readOutput);
        errorGobbler.start();
        outputGobbler.start();

        exitCode = 0;
        if (wait) {
            try { 
                process.waitFor();
                exitCode = process.exitValue();                 
            } catch (InterruptedException ex) { }
        }
        return exitCode;
    }   

    public int getExitCode() {
        return exitCode;
    }

    public boolean isOutputCompleted() {
        return (outputGobbler != null ? outputGobbler.isCompleted() : false);
    }

    public boolean isErrorCompleted() {
        return (errorGobbler != null ? errorGobbler.isCompleted() : false);
    }

    public String getOutput() {
        return (outputGobbler != null ? outputGobbler.getOutput() : null);        
    }

    public String getError() {
        return (errorGobbler != null ? errorGobbler.getOutput() : null);        
    }

//********************************************
//********************************************    

    /**
     * StreamGobbler reads inputstream to "gobble" it.
     * This is used by Executor class when running 
     * a commandline applications. Gobblers must read/purge
     * INSTR and ERRSTR process streams.
     * http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=4
     */
    private class StreamGobbler extends Thread {
        private InputStream is;
        private StringBuilder output;
        private volatile boolean completed; // mark volatile to guarantee a thread safety

        public StreamGobbler(InputStream is, boolean readStream) {
            this.is = is;
            this.output = (readStream ? new StringBuilder(256) : null);
        }

        public void run() {
            completed = false;
            try {
                String NL = System.getProperty("line.separator", "\r\n");

                InputStreamReader isr = new InputStreamReader(is);
                BufferedReader br = new BufferedReader(isr);
                String line;
                while ( (line = br.readLine()) != null) {
                    if (output != null)
                        output.append(line + NL); 
                }
            } catch (IOException ex) {
                // ex.printStackTrace();
            }
            completed = true;
        }

        /**
         * Get inputstream buffer or null if stream
         * was not consumed.
         * @return
         */
        public String getOutput() {
            return (output != null ? output.toString() : null);
        }

        /**
         * Is input stream completed.
         * @return
         */
        public boolean isCompleted() {
            return completed;
        }

    }

}

Aqui está um exemplo de leitura de saída do script.vbs mas trabalhos similares para scripts linux sh.

   ShellExec exec = new ShellExec(true, false);
   exec.execute("cscript.exe", null, true,
      "//Nologo",
      "//B",            // batch mode, no prompts
      "//T:320",        // timeout seconds
      "c:/my/script/test1.vbs",  // unix path delim works for script.exe
      "script arg 1",
      "script arg 2",
   );
   System.out.println(exec.getOutput());
Respondeu 23/09/2013 em 10:08
fonte usuário

votos
2

VerboseProcessclasse de utilitário de jcabi-log pode ajudá-lo:

String output = new VerboseProcess(
  new ProcessBuilder("executable with output")
).stdout();

A única dependência que você precisa:

<dependency>
  <groupId>com.jcabi</groupId>
  <artifactId>jcabi-log</artifactId>
  <version>0.7.5</version>
</dependency>
Respondeu 02/01/2013 em 16:45
fonte usuário

votos
2

Runtime.exec () retorna um objeto de processo, a partir do qual você pode extrair a saída de qualquer comando que você executou.

Respondeu 19/05/2009 em 14:33
fonte usuário

votos
1

Usando Runtime.exec dá-lhe um processo. Você pode usar estes getInputStream para obter o stdout deste processo, e colocar esse fluxo de entrada em um String, através de uma StringBuffer por exemplo.

Respondeu 19/05/2009 em 14:37
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more