在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
)。