package cn.exlive.monitor.utils; import cn.exlive.monitor.config.ProgramsConfig; import org.apache.commons.exec.CommandLine; import org.apache.commons.exec.DefaultExecuteResultHandler; import org.apache.commons.exec.DefaultExecutor; import org.apache.commons.exec.ExecuteResultHandler; import org.apache.commons.exec.ExecuteWatchdog; import org.apache.commons.exec.ProcessDestroyer; import org.apache.commons.exec.PumpStreamHandler; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.system.ApplicationHome; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.time.Duration; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; /** * 便捷得 Shell 执行工具类 * *
 *
 * Created by zhaopx.
 * Date: 2025/6/9
 * Time: 12:00
 * Vendor: exlive.cn
 *
 * 
* * @author zhaopx */ public class ShellUtils { private static final Logger logger = LoggerFactory.getLogger(ShellUtils.class); public static String INSTALL_PATH = getInstallPath(); public final static String OS = SystemUtils.IS_OS_LINUX ? "linux" : "windows"; private static ProcessBuilder processBuilder = new ProcessBuilder(); /** * 返回 App HOME * @return */ private static String getInstallPath() { String appHome = System.getenv("APP_HOME"); if(StringUtils.isBlank(appHome)) { appHome = System.getProperty("app.home"); } if(StringUtils.isBlank(appHome)) { // appHome = "D:/Work/Deploy"; ApplicationHome ah = new ApplicationHome(ShellUtils.class); // 获取jar包所在目录 File source = ah.getSource(); File dir = ah.getDir(); if(source == null && dir == null) { throw new IllegalStateException("请设置 Monitor APP_HOME 环境变量。"); } if(source == null && dir != null) { appHome = dir.getParentFile().getAbsolutePath(); } else { File appHomeFile = Optional.ofNullable(source.getParentFile()).map(File::getParentFile).orElseGet(()->{ String userDir = System.getProperty("user.dir"); return new File(org.springframework.util.StringUtils.hasLength(userDir) ? userDir : "."); }); appHome = appHomeFile.getAbsolutePath(); } logger.warn("use app home: {} recommen user set APP_HOME envirment.", appHome); } // monitor app home 的上一层是 INSTALL_DIR File homeDir = new File(appHome); String installPath = homeDir.getParentFile().getAbsolutePath(); logger.info("INSTALL_PATH: {}", installPath); File dataDir = new File(homeDir, "data"); if(!dataDir.exists()) { dataDir.mkdir(); logger.info("mkdir monitor data dir: {}", dataDir.getAbsolutePath()); } return installPath; } /** * @param pathOrCommand 脚本路径或者命令 * @return */ public static ExecResult exceShell(String pathOrCommand) { ExecResult result = new ExecResult(); StringBuffer stringBuffer = new StringBuffer(); try { // 执行脚本 Process ps = Runtime.getRuntime().exec(new String[]{"sh", "-c", pathOrCommand}); // 只能接收脚本echo打印的数据,并且是echo打印的最后一次数据 BufferedInputStream in = new BufferedInputStream(ps.getInputStream()); BufferedReader br = new BufferedReader(new InputStreamReader(in)); String line; while ((line = br.readLine()) != null) { stringBuffer.append(line); stringBuffer.append(System.lineSeparator()); } in.close(); br.close(); String execOut = stringBuffer.toString(); int exitValue = ps.waitFor(); result.setReturnCode(exitValue); if (0 == exitValue) { logger.info("{} command exec out is : {} {}", pathOrCommand, System.lineSeparator(), execOut); result.setExecResult(true); result.setExecOut(execOut); } else { result.setExecOut("call shell failed. error code is :" + exitValue); logger.error("{} command exec out is : {} {}", pathOrCommand, System.lineSeparator(), execOut); } } catch (Exception e) { result.setExecOut(e.getMessage()); e.printStackTrace(); } return result; } // 获取cpu架构 arm或x86 public static String getCpuArchitecture() { try { Process ps = Runtime.getRuntime().exec("arch"); StringBuffer stringBuffer = new StringBuffer(); int exitValue = ps.waitFor(); if (0 == exitValue) { // 只能接收脚本echo打印的数据,并且是echo打印的最后一次数据 BufferedInputStream in = new BufferedInputStream(ps.getInputStream()); BufferedReader br = new BufferedReader(new InputStreamReader(in)); String line; while ((line = br.readLine()) != null) { logger.info("脚本返回的数据如下: " + line); stringBuffer.append(line); } in.close(); br.close(); return stringBuffer.toString(); } } catch (Exception e) { e.printStackTrace(); } return null; } public static ExecResult execWithStatus(String workPath, List command, long timeout) { Process process = null; ExecResult result = new ExecResult(); try { processBuilder.directory(new File(workPath)); processBuilder.environment(); processBuilder.command(command); processBuilder.redirectErrorStream(true); process = processBuilder.start(); printOutput(process); boolean execResult = process.waitFor(timeout, TimeUnit.SECONDS); result.setReturnCode(process.exitValue()); if (execResult && process.exitValue() == 0) { logger.info("script execute success"); result.setExecResult(true); result.setExecOut("script execute success"); } else { result.setExecOut("script execute failed"); } return result; } catch (Exception e) { result.setExecErrOut(e.getMessage()); e.printStackTrace(); } return result; } public static ExecResult execWithLogs(String workPath, List command, long timeout) { Process process = null; ExecResult result = new ExecResult(); try { processBuilder.directory(new File(workPath)); processBuilder.environment(); processBuilder.command(command); processBuilder.redirectErrorStream(true); process = processBuilder.start(); String output = getOutput(process); boolean execResult = process.waitFor(timeout, TimeUnit.SECONDS); result.setReturnCode(process.exitValue()); result.setExecOut(output); if (execResult && process.exitValue() == 0) { logger.info("script execute success"); result.setExecResult(true); } else { } return result; } catch (Exception e) { result.setExecErrOut(e.getMessage()); e.printStackTrace(); } return result; } public static ExecResult execWithStatus(String workPath, List command, long timeout, Logger logger) { Process process = null; ExecResult result = new ExecResult(); try { processBuilder.directory(new File(workPath)); processBuilder.environment(); processBuilder.command(command); processBuilder.redirectErrorStream(true); process = processBuilder.start(); getOutput(process, logger); boolean execResult = process.waitFor(timeout, TimeUnit.SECONDS); result.setReturnCode(process.exitValue()); if (execResult && process.exitValue() == 0) { logger.info("script execute success"); result.setExecResult(true); result.setExecOut("script execute success"); } else { result.setExecOut("script execute failed"); } return result; } catch (Exception e) { result.setExecErrOut(e.getMessage()); e.printStackTrace(); } return result; } public static void getOutput(Process process, Logger logger) { CompletableFuture.runAsync(() -> { BufferedReader inReader = null; try { inReader = new BufferedReader(new InputStreamReader(process.getInputStream())); String line; StringBuffer stringBuffer = new StringBuffer(); while ((line = inReader.readLine()) != null) { stringBuffer.append(line); stringBuffer.append(System.lineSeparator()); } logger.info(stringBuffer.toString()); } catch (Exception e) { logger.error(e.getMessage(), e); } finally { closeQuietly(inReader); } BufferedReader errorReader = null; try { errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); String line; StringBuffer stringBuffer = new StringBuffer(); while ((line = errorReader.readLine()) != null) { stringBuffer.append(line); stringBuffer.append(System.lineSeparator()); } logger.error(stringBuffer.toString()); } catch (Exception e) { logger.error(e.getMessage(), e); } finally { closeQuietly(errorReader); } }); } public static String getOutput(Process process) { StringBuffer stringBuffer = new StringBuffer(); BufferedReader inReader = null; try { inReader = new BufferedReader(new InputStreamReader(process.getInputStream())); String line; while ((line = inReader.readLine()) != null) { stringBuffer.append(line); stringBuffer.append(System.lineSeparator()); } } catch (Exception e) { logger.error(e.getMessage(), e); } finally { closeQuietly(inReader); } return stringBuffer.toString(); } public static void printOutput(Process process) { CompletableFuture.runAsync(() -> { BufferedReader inReader = null; try { inReader = new BufferedReader(new InputStreamReader(process.getInputStream())); String line; StringBuffer stringBuffer = new StringBuffer(); while ((line = inReader.readLine()) != null) { stringBuffer.append(line); stringBuffer.append(System.lineSeparator()); } logger.trace(stringBuffer.toString()); } catch (Exception e) { logger.error(e.getMessage(), e); } finally { closeQuietly(inReader); } }); } public static String getError(Process process) { String errput = null; BufferedReader reader = null; try { if (process != null) { StringBuffer stringBuffer = new StringBuffer(); reader = new BufferedReader(new InputStreamReader(process.getErrorStream())); while (reader.read() != -1) { stringBuffer.append("\n" + reader.readLine()); } errput = stringBuffer.toString(); } } catch (Exception e) { e.printStackTrace(); } closeQuietly(reader); return errput; } public static ExecResult exec(String commands) { return exec(null, CommandLine.parse(commands), new HashMap<>(), -1, null); } public static ExecResult exec(String commands, int timeoutSecond) { return exec(null, CommandLine.parse(commands), new HashMap<>(), timeoutSecond, null); } public static ExecResult exec(String workPath, String commands) { return exec(workPath, CommandLine.parse(commands), new HashMap<>(), -1, null); } public static ExecResult exec(String workPath, String commands, int timeoutSecond) { return exec(workPath, CommandLine.parse( commands), new HashMap<>(), timeoutSecond, null); } /** * 执行命令,并返回回调信息 * @param workPath * @param commands * @param timeoutSecond * @param executeResultHandler * @return */ public static ExecResult exec(String workPath, String commands, int timeoutSecond, BrodcastExecuteResultHandler executeResultHandler) { return exec(workPath, CommandLine.parse( commands), new HashMap<>(), timeoutSecond, executeResultHandler); } public static ExecResult exec(String workPath, String commands, Map envs, int timeoutSecond) { return exec(workPath, CommandLine.parse( commands), envs, timeoutSecond, null); } /** * 执行命令,并返回回调信息 * @param workPath * @param commands * @param timeoutSecond * @param executeResultHandler * @return */ public static ExecResult exec(String workPath, String commands, Map envs, int timeoutSecond, BrodcastExecuteResultHandler executeResultHandler) { return exec(workPath, CommandLine.parse( commands), envs, timeoutSecond, executeResultHandler); } public static ExecResult exec(List commands) { return exec(null, CommandLine.parse(StringUtils.join(commands, " ")), new HashMap<>(), -1, null); } public static ExecResult exec(List commands, int timeoutSecond) { return exec(null, CommandLine.parse(StringUtils.join(commands, " ")), new HashMap<>(), timeoutSecond, null); } public static ExecResult exec(String workPath, List commands) { return exec(workPath, CommandLine.parse(StringUtils.join(commands, " ")), new HashMap<>(), -1, null); } public static ExecResult exec(String workPath, List commands, int timeoutSecond) { return exec(workPath, CommandLine.parse(StringUtils.join(commands, " ")), new HashMap<>(), timeoutSecond, null); } /** * 带有回调的执行命令 * @param workPath * @param commands * @param timeoutSecond * @param executeResultHandler * @return */ public static ExecResult exec(String workPath, List commands, int timeoutSecond, BrodcastExecuteResultHandler executeResultHandler) { return exec(workPath, CommandLine.parse(StringUtils.join(commands, " ")), new HashMap<>(), timeoutSecond, null); } public static ExecResult exec(String workPath, CommandLine commandLine, Map envs, int timeoutSecond, DefaultExecuteResultHandler handler) { DefaultExecutor executor = new DefaultExecutor(); executor.setExitValue(-1); // handler 记录 handler = (handler == null ? new DefaultExecuteResultHandler() : handler); ShellLogOutputStream output = new ShellLogOutputStream(); ShellLogOutputStream err = new ShellLogOutputStream(); executor.setStreamHandler(new PumpStreamHandler(output, err)); PidExecuteWatchdog watchDog = null; // 不限时间 if(timeoutSecond > 0) { watchDog = new PidExecuteWatchdog(Duration.ofSeconds(timeoutSecond).toMillis(), handler); executor.setWatchdog(watchDog); } String exeScript = "bash"; if (SystemUtils.IS_OS_WINDOWS) { // windows 系统不需要执行,也没有权限 exeScript = ""; } else { // Linux, Mac 系统使用 bash 直接执行脚本 exeScript = "bash"; } // String cmd = StringUtils.join(commands, " "); if(StringUtils.isNotBlank(workPath)) { logger.info("from path: {} execute cmd: {}", workPath, commandLine); executor.setWorkingDirectory(new File(workPath)); } else { logger.info("execute cmd: " + commandLine); } Map environment = new HashMap<>(System.getenv()); if(envs != null && !envs.isEmpty()) { environment.putAll(envs); } ExecResult result = new ExecResult(); final long start = System.currentTimeMillis(); try { executor.execute(commandLine, environment, handler); try { result.setPid(watchDog != null ? watchDog.getProcessPid() : -1); } catch (Exception ignore) {} if (timeoutSecond > 0) { handler.waitFor(Duration.ofSeconds(timeoutSecond)); } else { handler.waitFor(); } int exitCode = handler.getExitValue(); result.setUseTime(System.currentTimeMillis() - start); result.setExecOut(output.toString()); result.setReturnCode(exitCode); result.setExecErrOut(err.toString()); result.setExecResult(Objects.equals(0, exitCode)); return result; } catch (Exception e) { result.setUseTime(System.currentTimeMillis() - start); result.setExecResult(false); result.setReturnCode(-1); result.setExecOut(output.toString()); result.setExecErrOut(ExceptionUtils.getRootCauseMessage(e)); logger.error("execute: " + commandLine + " error.", e); return result; } } public static void closeQuietly(Reader reader) { try { if (reader != null) { reader.close(); } } catch (IOException ioe) { ioe.printStackTrace(); } } public static void destroy(Process process) { if (process != null) { process.destroyForcibly(); } } public static void addChmod(String path, String chmod) { ArrayList command = new ArrayList<>(); command.add("chmod"); command.add("-R"); command.add(chmod); command.add(path); execWithStatus(INSTALL_PATH, command, 60, logger); } public static void addChown(String path, String user, String group) { ArrayList command = new ArrayList<>(); command.add("chown"); command.add("-R"); command.add(user + ":" + group); command.add(path); execWithStatus(INSTALL_PATH, command, 60, logger); } }