简介
由于项目用到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);
}
总结
总结啥, 会用就行!!!