不灭的火

革命尚未成功,同志仍须努力下载JDK17

作者:AlbertWen  添加时间:2025-06-01 23:59:01  修改时间:2025-06-06 09:35:20  分类:07.Java框架/系统  编辑

附:Java使用Commons Exec执行本地命令行

在Java中使用Apache Commons Exec库实现逐行输出外部命令执行结果,可以通过自定义LogOutputStream并重写processLine()方法来实现。以下是详细步骤和完整示例:

1、添加Maven依赖

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-exec</artifactId>
    <version>1.3</version>
</dependency>

2、实现代码

import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteException;
import org.apache.commons.exec.ExecuteWatchdog;
import org.apache.commons.exec.LogOutputStream;
import org.apache.commons.exec.PumpStreamHandler;

import java.io.IOException;
import java.time.Duration;

public class CommandLineRunner {

    // 自定义输出流处理器(核心:逐行捕获输出)
    private static class LineOutputLogger extends LogOutputStream {
        @Override
        protected void processLine(String line, int logLevel) {
            System.out.println("[OUTPUT] " + line);  // 实时逐行打印
        }
    }

    public static void main(String[] args) throws IOException {
        // 1. 创建命令行对象(示例:执行ping命令)
        CommandLine cmd = CommandLine.parse("ping 127.0.0.1");
        // 对于复杂命令(含管道/重定向)使用:
        // cmd = new CommandLine("cmd.exe");
        // cmd.addArgument("/c");
        // cmd.addArgument("ping 127.0.0.1 && echo Complete", false);

        // 2. 配置执行器
        DefaultExecutor executor = new DefaultExecutor();
        executor.setStreamHandler(new PumpStreamHandler(
            new LineOutputLogger(),  // 标准输出处理器
            new LineOutputLogger()   // 错误输出处理器(可选:分开处理错误)
        ));
        executor.setWatchdog(ExecuteWatchdog.builder().setTimeout(Duration.ofSeconds(1800)).get());  // 超时:30小时,防止无限等待
        executor.setExitValue(0);  // 设置成功退出码

        try {
            //--------------------------------------
            // 3. 方式1:【同步】执行命令并获取退出码
            //--------------------------------------
            int exitCode = executor.execute(cmd);
            System.out.println("Exit Code: " + exitCode);

            //--------------------------------------
            // 4. 方式2:【异步】执行命令并获取退出码
            //--------------------------------------
            executor.execute(cmdLine, new ExecuteResultHandler() {
                @Override
                public void onProcessComplete(int i) {
                    System.out.println("【执行结果】" + i);
                }

                @Override
                public void onProcessFailed(ExecuteException e) {
                    System.out.println("【执行错误】" + e.getMessage());
                }
            });

        } catch (ExecuteException e) {
            System.err.println("执行失败: " + e.getMessage());
        }
    }
}

关键点解析

  1. LineOutputLogger类(核心):

    • 继承LogOutputStream并重写processLine()

    • 每次有新行输出时自动触发回调

    • logLevel参数:0=标准输出,1=错误输出(可按需区分处理)

  2. 流处理配置

    • PumpStreamHandler绑定自定义输出流

    • 第一个参数:标准输出处理器

    • 第二个参数:错误输出处理器(示例中统一处理)

  3. 命令构造技巧

    • 简单命令:CommandLine.parse("命令")

    • 复杂命令(含管道/重定向):

    • CommandLine cmd = new CommandLine("cmd.exe");
      cmd.addArgument("/c");
      cmd.addArgument("dir C:\\ && echo Complete", false);  // false表示不解析
      
  4. 超时控制

    • ExecuteWatchdog.builder().setTimeout(30_000).get() // 设置30秒超时

    • 超时自动终止进程

执行效果 

[OUTPUT] Pinging 127.0.0.1 with 32 bytes of data:
[OUTPUT] Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
[OUTPUT] Reply from 127.0.0.1: bytes=32 time<1ms TTL=128
...
Exit Code: 0

常见问题处理

  1. 命令被阻塞

    • 确保命令能自动结束(如ping -n 4限制次数)

    • 添加超时控制:executor.setWatchdog(ExecuteWatchdog.builder().setTimeout(10_000).get());

  2. 错误流单独处理

    • executor.setStreamHandler(new PumpStreamHandler(
          new LineOutputLogger(),  // 标准输出
          new LogOutputStream() {  // 错误输出专用处理器
              @Override
              protected void processLine(String line, int logLevel) {
                  System.err.println("[ERROR] " + line);
              }
          }
      ));
  3. 跨平台命令差异

    • Linux/macOS:使用/bin/sh -c "命令"

    • Windows:使用cmd.exe /c "命令"

注意:Commons Exec不直接支持交互式命令(如需要用户输入),此类场景需考虑其他方案(如ProcessBuilder)。