在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());
        }
    }
}
关键点解析
- 
	
LineOutputLogger类(核心):
- 
		
继承
LogOutputStream并重写processLine() - 
		
每次有新行输出时自动触发回调
 - 
		
logLevel参数:0=标准输出,1=错误输出(可按需区分处理) 
 - 
		
 - 
	
流处理配置:
- 
		
PumpStreamHandler绑定自定义输出流 - 
		
第一个参数:标准输出处理器
 - 
		
第二个参数:错误输出处理器(示例中统一处理)
 
 - 
		
 - 
	
命令构造技巧:
- 
		
简单命令:
CommandLine.parse("命令") - 
		
复杂命令(含管道/重定向):
 - 
		
CommandLine cmd = new CommandLine("cmd.exe"); cmd.addArgument("/c"); cmd.addArgument("dir C:\\ && echo Complete", false); // false表示不解析 
 - 
		
 - 
	
超时控制:
- 
		
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
常见问题处理
- 
	
命令被阻塞:
- 
		
确保命令能自动结束(如
ping -n 4限制次数) - 
		
添加超时控制:
executor.setWatchdog(ExecuteWatchdog.builder().setTimeout(10_000).get()); 
 - 
		
 - 
	
错误流单独处理:
- 
		
executor.setStreamHandler(new PumpStreamHandler( new LineOutputLogger(), // 标准输出 new LogOutputStream() { // 错误输出专用处理器 @Override protected void processLine(String line, int logLevel) { System.err.println("[ERROR] " + line); } } )); 
 - 
		
 - 
	
跨平台命令差异:
- 
		
Linux/macOS:使用
/bin/sh -c "命令" - 
		
Windows:使用
cmd.exe /c "命令" 
 - 
		
 
注意:Commons Exec不直接支持交互式命令(如需要用户输入),此类场景需考虑其他方案(如ProcessBuilder)。