Java commons-exec 执行外部命令

Java创建子进程(Process)执行外部命令底层的方法是new ProcessBuilder().start()或Runtime.getRuntime().exec()。

Apache commons-exec对底层进行封装,提供了更加详细的设置和监控方法。

pom.xml

1
2
3
4
5
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-exec</artifactId>
<version>1.3</version>
</dependency>

CmdHelper.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.CompletableFuture;

import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteWatchdog;
import org.apache.commons.exec.PumpStreamHandler;
import org.apache.commons.exec.ShutdownHookProcessDestroyer;

public class CmdHelper {
/**
* 执行外部命令,等待返回结果
*
* @param commandLine: 命令行
* @param out: 输出流,为空默认标准输出
* @param timeout: 超时,不大于0则不限超时
* @return CmdHandler based on DefaultExecuteResultHandler
*/
public static CmdHandler run(CommandLine commandLine, OutputStream out, long timeout) {
PumpStreamHandler pumpStreamHandler = null;
if (null == out) {
pumpStreamHandler = new PumpStreamHandler();
} else {
pumpStreamHandler = new PumpStreamHandler(out);
}

DefaultExecutor executor = new DefaultExecutor();
CmdHandler cmdHandler = new CmdHandler(executor);
executor.setStreamHandler(pumpStreamHandler);
ShutdownHookProcessDestroyer processDestroyer = new ShutdownHookProcessDestroyer();
executor.setProcessDestroyer(processDestroyer);// 随主进程退出

if (timeout <= 0) {
timeout = ExecuteWatchdog.INFINITE_TIMEOUT;
}
ExecuteWatchdog watchdog = new ExecuteWatchdog(timeout);
executor.setWatchdog(watchdog);// 控制超时

try {
executor.execute(commandLine, cmdHandler);
cmdHandler.waitFor();// 等待返回结果
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
return cmdHandler;
}

/**
* 异步执行外部命令
*
* @param commandLine: 命令行
* @param out: 输出流,为空默认标准输出
* @param timeout: 超时,不大于0则不限超时
* @return CompletableFuture<CmdHandler>
*/
public static CompletableFuture<CmdHandler> exec(CommandLine commandLine, OutputStream out, long timeout) {
CompletableFuture<CmdHandler> cf = new CompletableFuture<>();
PumpStreamHandler pumpStreamHandler = null;
if (null == out) {
pumpStreamHandler = new PumpStreamHandler();
} else {
pumpStreamHandler = new PumpStreamHandler(out);
}

DefaultExecutor executor = new DefaultExecutor();
CmdHandler cmdHandler = new CmdHandler(executor);
cmdHandler.setCallback(() -> {
cf.complete(cmdHandler);// 执行完成后回调
});
executor.setStreamHandler(pumpStreamHandler);
ShutdownHookProcessDestroyer processDestroyer = new ShutdownHookProcessDestroyer();
executor.setProcessDestroyer(processDestroyer);// 随主进程退出

if (timeout <= 0) {
timeout = ExecuteWatchdog.INFINITE_TIMEOUT;
}
ExecuteWatchdog watchdog = new ExecuteWatchdog(timeout);
executor.setWatchdog(watchdog);// 控制超时

try {
executor.execute(commandLine, cmdHandler);
} catch (IOException e) {
e.printStackTrace();
}
return cf;
}

public static void main(String[] args) throws InterruptedException {
CommandLine command = CommandLine.parse("ping 127.0.0.1 -t");

// 测试同步执行
CmdHandler result = CmdHelper.run(command, null, 3000);
System.out.println(result.resultString());

// 测试异步执行
CmdHelper.exec(command, null, 0).thenAccept(cmdHandler -> {
System.out.println(cmdHandler.resultString());
});
}
}

CmdHandler.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import org.apache.commons.exec.DefaultExecuteResultHandler;
import org.apache.commons.exec.ExecuteException;
import org.apache.commons.exec.ExecuteWatchdog;
import org.apache.commons.exec.Executor;

public class CmdHandler extends DefaultExecuteResultHandler {
Executor executor;
Runnable callback;

public CmdHandler(Executor executor) {
this.executor = executor;
}

public void setCallback(Runnable callback) {
this.callback = callback;
}

public Executor getExecutor() {
return this.executor;
}

public ExecuteWatchdog getWatchdog() {
if (this.executor == null) return null;
return this.executor.getWatchdog();
}

public String resultString() {
String retMsg = "complete";
if (this.getException() != null) {
ExecuteWatchdog watchdog = this.getWatchdog();
if (watchdog != null && watchdog.killedProcess()) {
retMsg = "timeout";
} else {
retMsg = this.getException().getMessage();
}
}
return this.getExitValue() + ":" + retMsg;
}

@Override
public void onProcessComplete(int exitValue) {
super.onProcessComplete(exitValue);
if (callback != null) {
callback.run();
}
}

@Override
public void onProcessFailed(ExecuteException e) {
super.onProcessFailed(e);
if (callback != null) {
callback.run();
}
}
}

参考

  1. https://www.cnblogs.com/kingcucumber/p/3180146.html
  2. https://www.jianshu.com/p/73aaec23009d