Java执行外部命令(commons exec) - zhouted/zhouted.github.io GitHub Wiki

标签: java commons exec shell process CompletableFuture


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

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

pom.xml

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

CmdHelper.java

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

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
⚠️ **GitHub.com Fallback** ⚠️