commons-exec命令调用

简介

由于项目用到ffmpeg等命令, 需要调用本地命令。之前只是简单的System.getRuntime().exec(..)来运行, 结果会出现各种问题, 比如命令卡死不退出, 命令运行失败, 程序却以为运行正常等等。

apache commons exec可以让我们轻松的从Java应用程序中启动一些子流程, 并更好的控制子流程。

demo

命令: ping www.baidu.com -t

maven依赖

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-exec -->
<dependency>
	<groupId>org.apache.commons</groupId>
	<artifactId>commons-exec</artifactId>
	<version>1.3</version>
</dependency>

程序

public static void main(String[] args) throws IOException {
    System.out.println("==================");
    CommandLine cmdLine = new CommandLine("ping");
    cmdLine.addArgument("${host}");
    cmdLine.addArgument("-t");
    cmdLine.setSubstitutionMap(Collections.singletonMap("host","www.baidu.com"));

    // 阻塞
    DefaultExecutor defaultExecutor = new DefaultExecutor();
    // 看门狗(timeout)
    ExecuteWatchdog watchdog = new ExecuteWatchdog(10 * 1000);

    defaultExecutor.setWatchdog(watchdog);
    defaultExecutor.setExitValue(1);
    // 获取命令运行中的流程(一般流操作会在多线程中操作)
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    ByteArrayOutputStream errorOutPutStream = new ByteArrayOutputStream();
    PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(outputStream, errorOutPutStream);

    defaultExecutor.setStreamHandler(pumpStreamHandler);
    int exitValue = defaultExecutor.execute(cmdLine);
    System.out.println(exitValue);


    System.out.println(outputStream.toString("gbk"));
    System.out.println(errorOutPutStream.toString("gbk"));
}

新增记录

添加时间: 2020年12月18日16:09:23


import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Map;

import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecuteResultHandler;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteException;
import org.apache.commons.exec.ExecuteWatchdog;
import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler;
import org.apache.commons.lang3.tuple.Pair;

import lombok.extern.log4j.Log4j2;

@Log4j2
public class ShellUtils {

	/**
	 * 
	 * @param cmdLine
	 * @param substitutionMap
	 * @param exitValue
	 * @param resultHandler
	 * @param timeoutInMillisecond 
	 * @throws ExecuteException
	 * @throws IOException
	 * @throws InterruptedException 
	 */
	public static void exec(CommandLine cmdLine, int exitValue, DefaultExecuteResultHandler resultHandler, int timeoutInMillisecond) throws ExecuteException, IOException, InterruptedException {
		System.out.println(cmdLine.getExecutable());
		ExecuteWatchdog watchdog = new ExecuteWatchdog(timeoutInMillisecond);
		Executor executor = new DefaultExecutor();
		executor.setExitValue(exitValue);
		executor.setWatchdog(watchdog);
		
		if (resultHandler != null) {
			// 如果使用的默认 DefaultExecuteResultHandler, 线程就是异步执行的
			executor.execute(cmdLine, resultHandler);
		} else {
		    // 这边线程会阻塞
			executor.execute(cmdLine);
		}
	}
	
	
	/**
	 * 
	 * @param cmdLine
	 * @param substitutionMap
	 * @param exitValue
	 * @param resultHandler
	 * @param timeoutInMillisecond 
	 * @throws ExecuteException
	 * @throws IOException
	 * @throws InterruptedException 
	 */
	public static void execBlock(CommandLine cmdLine, int exitValue, DefaultExecuteResultHandler resultHandler, int timeoutInMillisecond) throws ExecuteException, IOException, InterruptedException {
		long start = System.currentTimeMillis();
		System.out.println(start+"-cmd: run start");
		if(resultHandler == null){
			resultHandler = new DefaultExecuteResultHandler();
		}
		exec(cmdLine, exitValue, resultHandler, timeoutInMillisecond);
		resultHandler.waitFor();
		System.out.println("--cmd: run end 耗时:" + (System.currentTimeMillis() - start));
	}
	
	
	/**
	 * 返回错误流的输出, 注意不要运行时间太长的命令, 否者会卡死
	 * @param cmdLine
	 * @param substitutionMap
	 * @param exitValue
	 * @param millisecond
	 * @return
	 * @throws ExecuteException
	 * @throws IOException
	 */
	public static Pair<String, String> execRetrunConsoleStr(CommandLine cmdLine, Map<String, String> substitutionMap,int exitValue, int millisecond, String charset) throws ExecuteException, IOException {
		cmdLine.setSubstitutionMap(substitutionMap);
		ExecuteWatchdog watchdog = new ExecuteWatchdog(millisecond);
		Executor executor = new DefaultExecutor();
		executor.setExitValue(exitValue);
		executor.setWatchdog(watchdog);
		executor.execute(cmdLine);
		// 获取命令运行中的流程(一般流操作会在多线程中操作)
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		ByteArrayOutputStream errorOutPutStream = new ByteArrayOutputStream();
		PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(outputStream, errorOutPutStream);
		executor.setStreamHandler(pumpStreamHandler);
		int execute = executor.execute(cmdLine);
		String errorOutputStr = errorOutPutStream.toString(charset);
		String outputStr = outputStream.toString(charset);
		Pair<String, String> of = Pair.of(errorOutputStr, outputStr);
		return of;
	}

}

如果我们新建了一个类继承 DefaultExecuteResultHandler, 那么在重写onProcessFailed 和onProcessComplete 方法的时候, 必须调用

    @Override
	public void onProcessFailed(ExecuteException e) {
		//  我们的业务逻辑
		// 这个必须加, 否者程序不退出
		super.onProcessFailed(e);
	}
	
	@Override
	public void onProcessComplete(int exitValue) {
	  //  我们的业务逻辑
	  // 这个必须加, 否者程序不退出
	  super.onProcessComplete(exitValue);
	}

总结

总结啥, 会用就行!!!

demo项目地址