Подтвердить что ты не робот

Запуск stdout при вызове Runtime.exec

При возникновении проблем с сетью на клиентских компьютерах я хотел бы иметь возможность запускать несколько командных строк и отправлять результаты по электронной почте себе.

Я нашел Runtime.exec позволит мне выполнять произвольные команды, но сбор результатов в String интереснее.

Я понимаю, что могу перенаправить вывод в файл, а затем прочитать из файла, но мой здравый смысл говорит мне, что там более элегантный способ сделать это.

Предложения?

4b9b3361

Ответ 1

Вам нужно записать как std out, так и std err в процессе. Затем вы можете записать std в файл/почту или подобное.

Подробнее см. в этой статье и, в частности, обратите внимание на механизм StreamGobbler, который фиксирует stdout/err в отдельных потоках. Это необходимо для предотвращения блокировки и является источником многочисленных ошибок, если вы не делаете это правильно!

Ответ 2

Используйте ProcessBuilder. После вызова start() вы получите объект Process, из которого вы можете получить потоки stderr и stdout.

UPDATE: ProcessBuilder дает вам больше контроля; Вам не обязательно использовать его, но мне легче в долгосрочной перспективе. В частности, возможность перенаправления stderr на stdout, что означает, что вам нужно только отсоединить один поток.

Ответ 3

Используйте Plexus Utils, он используется Maven для выполнения всех внешних процессов.

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
}

Ответ 4

Для процессов, которые не генерируют много выходных данных, я думаю, что это простое решение, которое использует Apache IOUtils:

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

Предостережение. Однако, если ваш процесс генерирует много выходных данных, этот подход может вызвать проблемы, как указано в Process class JavaDoc:

Созданный подпроцесс не имеет собственного терминала или консоли. Все его стандартные операции io (т.е. Stdin, stdout, stderr) будут перенаправлены на родительский процесс через три потока (getOutputStream(), getInputStream(), getErrorStream()). Родительский процесс использует эти потоки для подачи ввода и получения вывода из подпроцесса. Поскольку некоторые собственные платформы обеспечивают ограниченный размер буфера для стандартных потоков ввода и вывода, неспособность быстро записать поток ввода или прочитать выходной поток подпроцесса, может привести к блокировке подпроцесса и даже к взаимоблокировке.

Ответ 5

Runtime.exec() возвращает объект Process, из которого вы можете извлечь результат любой команды, которую вы выполнили.

Ответ 6

VerboseProcess класс утилиты из jcabi-log может помочь вам:

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

Единственная зависимость, в которой вы нуждаетесь:

<dependency>
  <groupId>com.jcabi</groupId>
  <artifactId>jcabi-log</artifactId>
  <version>0.7.5</version>
</dependency>

Ответ 7

Это мой вспомогательный класс, который используется в течение многих лет. Один маленький класс. Он имеет класс JavaWorld streamgobbler для исправления утечек ресурсов JVM. Не знаю, действительны ли все еще для JVM6 и JVM7, но это не повредит. Помощник может читать выходной буфер для последующего использования.

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 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;
        }

    }

}

Вот пример чтения вывода из .vbs script, но аналогичный работает для сценариев 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());

Ответ 8

Использование Runtime.exec предоставляет вам процесс. Вы можете использовать getInputStream, чтобы получить стандартный вывод этого процесса, и, например, поместить этот поток ввода в String через StringBuffer.