调用 Runtime.exec 时捕获标准输出

发布于 2024-07-20 12:42:07 字数 183 浏览 4 评论 0原文

当客户端计算机上遇到网络问题时,我希望能够运行一些命令行并将结果通过电子邮件发送给自己。

我发现 Runtime.exec 将允许我执行任意命令,但将结果收集到字符串中更有趣。

我意识到我可以将输出重定向到文件,然后从文件中读取,但我的敏锐感觉告诉我有一种更优雅的方法可以做到这一点。

建议?

When experiencing networking problems on client machines, I'd like to be able to run a few command lines and email the results of them to myself.

I've found Runtime.exec will allow me to execute arbitrary commands, but Collecting the results in a String is more interesting.

I realize I could redirect output to a file, and then read from the file, but my spidey sense is telling me there's a more elegant way of doing it.

Suggestions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

妥活 2024-07-27 12:42:07

您需要捕获过程中的 std out 和 std err。 然后您可以将 std 写入文件/邮件或类似文件。

有关详细信息,请参阅本文 ,特别要注意在单独线程中捕获 stdout/err 的 StreamGobbler 机制。 这对于防止阻塞至关重要,如果操作不当,则会导致许多错误!

You need to capture both the std out and std err in the process. You can then write std out to a file/mail or similar.

See this article for more info, and in particular note the StreamGobbler mechanism that captures stdout/err in separate threads. This is essential to prevent blocking and is the source of numerous errors if you don't do it properly!

水染的天色ゝ 2024-07-27 12:42:07

使用 ProcessBuilder。 调用 start() 后,您将得到一个 Process 对象,您可以从中获取 stderr 和 stdout 流。

更新:ProcessBuilder 为您提供更多控制; 您不必使用它,但从长远来看,我发现它更容易。 特别是能够将 stderr 重定向到 stdout,这意味着您只需吸收一个流。

Use ProcessBuilder. After calling start() you'll get a Process object from which you can get the stderr and stdout streams.

UPDATE: ProcessBuilder gives you more control; You don't have to use it but I find it easier in the long run. Especially the ability to redirect stderr to stdout which means you only have to suck down one stream.

〗斷ホ乔殘χμё〖 2024-07-27 12:42:07

对于不会产生太多输出的进程,我认为这个简单的解决方案利用 Apache IOUtils 就足够了:

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

警告:但是,如果您的进程生成大量输出,则此方法可能会导致问题,如 流程类 JavaDoc

创建的子进程没有自己的终端或控制台。 它的所有标准io(即stdin、stdout、stderr)操作都会通过三个流(getOutputStream()、getInputStream()、getErrorStream())重定向到父进程。 父进程使用这些流向子进程提供输入并从子进程获取输出。 由于某些原生平台只为标准输入输出流提供有限的缓冲区大小,因此未能及时写入子进程的输入流或读取子进程的输出流可能会导致子进程阻塞,甚至死锁。

For processes that don't generate much output, I think this simple solution that utilizes Apache IOUtils is sufficient:

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

Caveat: However, if your process generates a lot of output, this approach may cause problems, as mentioned in the Process class JavaDoc:

The created subprocess does not have its own terminal or console. All its standard io (i.e. stdin, stdout, stderr) operations will be redirected to the parent process through three streams (getOutputStream(), getInputStream(), getErrorStream()). The parent process uses these streams to feed input to and get output from the subprocess. Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, and even deadlock.

酒废 2024-07-27 12:42:07

使用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
}

Use Plexus Utils, it is used by Maven to execut all external processes.

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
}
不及他 2024-07-27 12:42:07

这是我多年来一直使用的助手类。 一小班。 它有 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'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;
        }

    }

}

以下是从 .vbs 脚本读取输出的示例,但 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());

This is my helper class been using for years. One small class. It has JavaWorld streamgobbler class to fix JVM resource leaks. Don't know if still valid for JVM6 and JVM7 but does not hurt. Helper can read output buffer for later use.

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

    }

}

Here is an example reading output from .vbs script but similar works for linux sh scripts.

   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());
爱冒险 2024-07-27 12:42:07

VerboseProcess< 可以帮助您:

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

jcabi-log 中的 /a> 实用程序类 你需要的依赖:

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

VerboseProcess utility class from jcabi-log can help you:

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

The only dependency you need:

<dependency>
  <groupId>com.jcabi</groupId>
  <artifactId>jcabi-log</artifactId>
  <version>0.7.5</version>
</dependency>
万劫不复 2024-07-27 12:42:07

Using Runtime.exec gives you a process. You can these use getInputStream to get the stdout of this process, and put this input stream into a String, through a StringBuffer for example.

旧话新听 2024-07-27 12:42:07

Runtime.exec() 返回一个 Process 对象,您可以从中提取您运行的任何命令的输出。

Runtime.exec() returns a Process object, from which you can extract the output of whatever command you ran.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文