java调用shell获取错误信息_java 执行shell命令及日志收集避坑指南

本文介绍了Java如何执行shell命令,重点讨论了使用Runtime.exec()和ProcessBuilder的区别,强调了调用shell命令时应考虑的事项,如进程管理、内存使用、日志收集和异常处理。并提供了一个完整的shell调用示例,实现了安全可靠的shell运行功能,支持日志输出和异常处理。
摘要由CSDN通过智能技术生成

 有时候我们需要调用系统命令执行一些东西,可能是为了方便,也可能是没有办法必须要调用。涉及执行系统命令的东西,则就不能做跨平台了,这和java语言的初衷是相背的。

  废话不多说,java如何执行shell命令?自然是调用java语言类库提供的接口API了。

1. java执行shell的api

  执行shell命令,可以说系统级的调用,编程语言自然必定会提供相应api操作了。在java中,有两个api供调用:Runtime.exec(), Process API. 简单使用如下:

1.1. Runtime.exec() 实现

  调用实现如下:

import java.io.InputStream;
 
public class RuntimeExecTest {
    @Test
    public static void testRuntimeExec() {
        try {
            Process process = Runtime.getRuntime()
                                .exec("cmd.exe /c dir");
            process.waitFor();
        } 
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

  简单的说就是只有一行调用即可:Runtime.getRuntime().exec("cmd.exe /c dir") ; 看起来非常简洁。

 1.2. ProcessBuilder 实现

  使用ProcessBuilder需要自己操作更多东西,也因此可以自主设置更多东西。(但实际上底层与Runtime是一样的了),用例如下:

public class ProcessBuilderTest {
     
    @Test
    public void testProcessBuilder() {
        ProcessBuilder processBuilder = new ProcessBuilder();
        processBuilder.command("ipconfig");
        //将标准输入流和错误输入流合并,通过标准输入流读取信息
        processBuilder.redirectErrorStream(true);
        try {
            //启动进程
            Process start = processBuilder.start();
            //获取输入流
            InputStream inputStream = start.getInputStream();
            //转成字符输入流
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "gbk");
            int len = -1;
            char[] c = new char[1024];
            StringBuffer outputString = new StringBuffer();
            //读取进程输入流中的内容
            while ((len = inputStreamReader.read(c)) != -1) {
                String s = new String(c, 0, len);
                outputString.append(s);
                System.out.print(s);
            }
            inputStream.close();
        } 
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}

  看起来是要麻烦些,但实际上是差不多的,只是上一个用例没有处理输出日志而已。但总体来说的 ProcessBuilder 的可控性更强,所以一般使用这个会更自由些。

  以下Runtime.exec()的实现:

// java.lang.Runtime#exec
    public Process exec(String[] cmdarray, String[] envp, File dir)
        throws IOException {
        // 仅为 ProcessBuilder 的一个封装
        return new ProcessBuilder(cmdarray)
            .environment(envp)
            .directory(dir)
            .start();
    }

2. 调用shell思考事项

  从上面来看,要调用系统命令,并非难事。那是否就意味着我们可以随便调用现成方案进行处理工作呢?当然不是,我们应当要考虑几个问题?

    1. 调用系统命令是进程级别的调用;
      进程与线程的差别大家懂的,更加重量级,开销更大。在java中,我们更多的是使用多线程进行并发。但如果用于系统调用,那就是进程级并发了,而且外部进程不再受jvm控制,出了问题也就不好玩了。所以,不要随便调用系统命令是个不错的实践。
    2. 调用系统命令是硬件相关的调用;
      java语言的思想是一次编写,到处使用。但如果你使用的系统调用,则不好处理了,因为每个系统支持的命令并非完全一样的,你的代码也就会因环境的不一样而表现不一致了。健壮性就下来了,所以,少用为好。
    3. 内存是否够用?
      一般我们jvm作为一个独立进程运行,会被分配足够多的内存,以保证运行的顺畅与高效。这时,可能留给系统的空间就不会太多了,而此时再调用系统进程运行业务,则得提前预估下咯。
    4. 进程何时停止?
      当我调起一个系统进程之后,我们后续如何操作?比如是异步调用的话,可能就忽略掉结果了。而如果是同步调用的话,则当前线程必须等待进程退出,这样会让我们的业务大大简单化了。因为异步需要考虑的事情往往很多。
    5. 如何获取进程日志信息?
      一个shell进程的调用,可能是一个比较耗时的操作,此时应该是只要任何进度,就应该汇报出来,从而避免外部看起来一直没有响应,从而无法判定是死掉了还是在运行中。而外部进程的通信,又不像一个普通io的调用,直接输出结果信息。这往往需要我们通过两个输出流进行捕获。而如何读取这两个输出流数据,就成了我们获取日志信息的关键了。ProcessBuilder

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值