Java Runtime 类详解

这篇具有很好参考价值的文章主要介绍了Java Runtime 类详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. Runtime 介绍

Runtime 是 Java 提供的一个启动子进程来执行命令的方式,它提供了 6 个重载的 exec 方法,用于单独启动一个子进程来执行命令或调用程序。

  • public Process exec(String command) throws IOException
  • public Process exec(String command, String[] envp) throws IOException
  • public Process exec(String command, String[] envp, File dir) throws IOException
  • public Process exec(String cmdarray[]) throws IOException
  • public Process exec(String[] cmdarray, String[] envp) throws IOException
  • public Process exec(String[] cmdarray, String[] envp, File dir) throws IOException

每一个方法最终都返回一个 Process 对象表示一个进程,从该对象中能够获取进程执行的退出状态码,标准输出流和标准错误流,进而获取进程执行的输出内容。

前 5 个方法都是间接调用最后一个方法 public Process exec(String[] cmdarray, String[] envp, File dir) throws IOException 。该方法有 3 个参数:

  • cmdarray 命令字符串数组表示,例如执行命令 echo "hello world",对应的参数为 new String[]{"echo","hello world"}
  • envp 字符串数组(可以为 null),表示命令执行过程中设置的环境变量,格式为 name=value,例如:a=1。当为 null 时,(应该,但不一定,取决于 jvm 的实现)继承当前 jvm 进程的环境变量。
  • dir 是一个 File 对象(可以为 null),表示命令执行的工作目录。如果命令中有使用到类似于 ./ 的相对路径,则该相对路径就是基于 dir 的。当为 null 时,工作目录(应该,但不一定,取决于 jvm 的实现)继承当前 jvm 进程的工作目录。

2. 命令使用一个字符串和使用字符串数组的区别

public Process exec(String command, String[] envp, File dir) throws IOException

public Process exec(String[] cmdarray, String[] envp, File dir) throws IOException
的区别。

public Process exec(String command, String[] envp, File dir) throws IOException 的实现如下:

public Process exec(String command, String[] envp, File dir) throws IOException {
    if (command.isEmpty())
        throw new IllegalArgumentException("Empty command");

    //使用空格对字符串进行分割
    StringTokenizer st = new StringTokenizer(command);
    String[] cmdarray = new String[st.countTokens()];
    for (int i = 0; st.hasMoreTokens(); i++)
        cmdarray[i] = st.nextToken();
    return exec(cmdarray, envp, dir);
}

可以发现,它将 command 命令使用 StringTokenizer空格进行分割,然后组装成数组 cmdarray,最终依然调用的是 exec(String[] cmdarray, String[] envp, File dir) 方法。

如果不仔细思考,可能觉得这两个重载方法没什么区别,exec(String[] cmdarray, String[] envp, File dir) 不是多此一举吗?

我们看一个在 linux 系统执行日期修改的命令:

date -s "2022-11-11 12:00:00"

很遗憾使用 exec(String command, String[] envp, File dir) 方法执行该命令无法成功:

//无法执行成功,会报错
Runtime.getRuntime().exec("date -s \"2022-11-11 12:00:00\"", null, null);

这是因为该方法将命令以空格进行分割然后组成数组进行执行,实际上在真正调用
exec(cmdarray, envp, dir) 为:

Runtime.getRuntime().exec(new String[]{"date", "-s", "\"2022-11-11", "12:00:00\"", null, null);

它将参数 "2022-11-11 12:00:00" 分割成了两个参数 "2022-11-1112:00:00"。所以执行命令时就导致了报错。

现在应该理解了单字符串命令和命令数组的区别,总结如下:

  • 如果执行的命令的参数中包含空格一定要使用 exec(String[] cmdarray, String[] envp, File dir),否则将导致命令执行失败。
  • 如果执行的命令的参数中没有空格不会导致参数分裂,则两个方法都一样

3. Runtime 工具类封装

下面是作者常用的 Runtime 工具类 SystemCommandUtil

package com.cssth.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;

/**
 * 系统命令执行
 *
 * @author geng
 * @since 2022/3/22 9:49
 */
public final class SystemCommandUtil {
    private final static Logger logger = LoggerFactory.getLogger(SystemCommandUtil.class);

    /**
     * 不要执行<code>cmd</code>等长时间不会退出的命令,否则会导致启动的子进程长时间不退出!
     *
     * @param command 命令
     */
    public static void exec(String command) {
        execWithExitCode(command);
    }

    public static int execWithExitCode(String command) {
        CommandExecResult commandExecResult = execWithExitCodeAndResult(command);
        return commandExecResult.exitCode;
    }

    public static CommandExecResult execWithExitCodeAndResult(String[] cmdarray) {
        InputStream errorStream;
        InputStream inputStream;
        int exitCode = -1;
        String stdinOutput = "";
        String errorOutput = "";
        try {
            Process exec = Runtime.getRuntime().exec(cmdarray);
            inputStream = exec.getInputStream();
            stdinOutput = readStringFromInputStream(inputStream);
            logger.warn("执行命令'{}'标准流输出:\n{}", cmdarray, stdinOutput);
            //有些命令就算执行成功也会将输出放到错误流,所以这里也将错误流内容进行读取,便于调试
            errorStream = exec.getErrorStream();
            errorOutput = readStringFromInputStream(errorStream);
            logger.warn("执行命令'{}'错误流输出:\n{}", cmdarray, errorOutput);

            exitCode = exec.waitFor();
            if (exitCode != 0) {
                logger.warn("执行命令'{}'可能失败,退出code:{}!", Arrays.toString(cmdarray), exitCode);
            } else {
                logger.warn("执行命令'{}'成功,退出code:{}!", Arrays.toString(cmdarray), exitCode);
            }
        } catch (InterruptedException ie) {
            logger.error("执行命令'{}'被中断!", Arrays.toString(cmdarray));
        } catch (IOException e) {
            logger.error("执行命令'" + Arrays.toString(cmdarray) + "'抛出异常!", e);
        }
        return new CommandExecResult(exitCode, stdinOutput, errorOutput);
    }

    public static CommandExecResult execWithExitCodeAndResult(String command) {
        InputStream errorStream;
        InputStream inputStream;
        int exitCode = -1;
        String stdinOutput = "";
        String errorOutput = "";
        try {
            Process exec = Runtime.getRuntime().exec(command);
            inputStream = exec.getInputStream();
            stdinOutput = readStringFromInputStream(inputStream);
            logger.warn("执行命令'{}'标准流输出:\n{}", command, stdinOutput);
            //有些命令就算执行成功也会将输出放到错误流,所以这里也将错误流内容进行读取,便于调试
            errorStream = exec.getErrorStream();
            errorOutput = readStringFromInputStream(errorStream);
            logger.warn("执行命令'{}'错误流输出:\n{}", command, errorOutput);

            exitCode = exec.waitFor();
            if (exitCode != 0) {
                logger.warn("执行命令'{}'可能失败,退出code:{}!", command, exitCode);
            } else {
                logger.warn("执行命令'{}'成功,退出code:{}!", command, exitCode);
            }
        } catch (InterruptedException ie) {
            logger.error("执行命令'{}'被中断!", command);
        } catch (IOException e) {
            logger.error("执行命令'" + command + "'抛出异常!", e);
        }
        return new CommandExecResult(exitCode, stdinOutput, errorOutput);
    }

    private static String readStringFromInputStream(InputStream stream) throws IOException {
        byte[] buff = new byte[128];
        int n;
        StringBuilder sb = new StringBuilder();
        try {
            while ((n = stream.read(buff)) != -1) {
                sb.append(new String(buff, 0, n));
            }
        } finally {
            stream.close();
        }
        return sb.toString();
    }

    @SuppressWarnings("unused")
    public static class CommandExecResult {
        private int exitCode;
        private String stdinOutput;
        private String stderrOutput;

        public CommandExecResult() {
        }

        public CommandExecResult(int exitCode, String stdinOutput, String stderrOutput) {
            this.exitCode = exitCode;
            this.stdinOutput = stdinOutput;
            this.stderrOutput = stderrOutput;
        }

        public int getExitCode() {
            return exitCode;
        }

        public void setExitCode(int exitCode) {
            this.exitCode = exitCode;
        }

        public String getStdinOutput() {
            return stdinOutput;
        }

        public void setStdinOutput(String stdinOutput) {
            this.stdinOutput = stdinOutput;
        }

        public String getStderrOutput() {
            return stderrOutput;
        }

        public void setStderrOutput(String stderrOutput) {
            this.stderrOutput = stderrOutput;
        }
    }
}

该工具类能够很方便的执行命令并获取命令退出码和输出信息。文章来源地址https://www.toymoban.com/news/detail-469428.html

到了这里,关于Java Runtime 类详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包