Java调用命令行并返回打印的内容

news2025/1/19 7:03:07

博主在最近的工作中,收到了这样一个需求。

调用别人以前完成开发的 jar 包或 python 程序,并将原程序在命令行中输出的内容封装为 JSON 对象后通过 RESTFul 接口返回。

面对以上的需求,博主给出了以下解决方案。话不多说,上代码。

import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.*;

/**
 * <p>
 * Java 在执行 Runtime.getRuntime().exec(command)之后,Linux会创建一个进程,
 * 该进程与JVM进程建立三个管道连接,标准输入流、标准输出流、标准错误流。
 * </p>
 *
 * @author SupremeSir
 * @since 2022/11/27
 */
@Slf4j
public class ShellCommandExecutor {
    /**
     * 创建线程池
     */
    private static final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(2, 4, 20,TimeUnit.MINUTES, new ArrayBlockingQueue<>(10), new ThreadPoolExecutor.CallerRunsPolicy());

    /**
     * @param cmd 命令行内容
     */
    public static ShellExecutorResultBean execShellStrWithResult(String cmd) {
        // 命令行输出的正常信息
        String sysOutInfo = null;
        // 命令行输出的错误信息
        String sysOutError = null;

        // 判断系统环境
        boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");

        // 组装命令行命令
        String[] cmdArray = {isWindows ? "cmd" : "/bin/sh", isWindows ? "/c" : "-c", cmd};
        String charset = isWindows ? "GBK" : "UTF-8";

        try {
            // 执行命令
            Process process = Runtime.getRuntime().exec(cmdArray);

            // 启动子线程,读取命令行输出的正常信息
            FutureTask<String> sysOutTask = new FutureTask<>(() -> {
                StringBuilder builder = new StringBuilder();
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), charset))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        builder.append(line);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return builder.toString();
            });
            THREAD_POOL_EXECUTOR.execute(sysOutTask);

            // 启动子线程,命令行输出的错误信息
            FutureTask<String> sysErrorTask = new FutureTask<>(() -> {
                StringBuilder builder = new StringBuilder();
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream(), charset))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        builder.append(line);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return builder.toString();
            });
            THREAD_POOL_EXECUTOR.execute(sysErrorTask);

            log.info("命令行执行进程开始等待");
            process.waitFor();

            // 获取命令行输出信息
            sysOutInfo = sysOutTask.get();
            sysOutError = sysErrorTask.get();

            log.info("命令行执行进程结束等待");

            // 判断命令行进程执行状态
            if (process.isAlive()) {
                process.destroy();
                log.info("线程已销毁:" + process.isAlive());
            } else {
                log.info("线程已销毁:" + process.isAlive());
            }

        } catch (InterruptedException | IOException | ExecutionException e) {
            e.printStackTrace();
        }

        // 组装返回结果
        ShellExecutorResultBean resultBean = new ShellExecutorResultBean();
        resultBean.setSysOutError(sysOutError);
        resultBean.setSysOutInfo(sysOutInfo);

        return resultBean;
    }

    /**
     * 命令行输出结果包装类
     */
    public static class ShellExecutorResultBean{
        /**
         * 命令行输出的正常信息
         */
        private String sysOutInfo;
        /**
         * 命令行输出的错误信息
         */
        private String sysOutError;

        public String getSysOutInfo() {
            return sysOutInfo;
        }

        public void setSysOutInfo(String sysOutInfo) {
            this.sysOutInfo = sysOutInfo;
        }

        public String getSysOutError() {
            return sysOutError;
        }

        public void setSysOutError(String sysOutError) {
            this.sysOutError = sysOutError;
        }

        @Override
        public String toString() {
            return "ShellExecutorResultBean{" +
                    "sysOutInfo='" + sysOutInfo + '\'' +
                    ", sysOutError='" + sysOutError + '\'' +
                    '}';
        }
    }
}

注意:

  1. 如果你不需要获取命令行的数据,也最好将命令行的输出信息、错误信息,都通过线程的方式获取一下,这样可以避免因输入流缓冲区灌满而导致的死锁问题。

  2. 如果你在 Linux 环境中执行调用,最好使用 /bin/sh 解析 shell 命令,否则可能也会导致线程莫名卡死的状态。

  3. 如果你使用的是 XShell 连接工具,同时规避了以上两个问题,但线程还是莫名其妙的卡死了,且收到 “转发 X11Xmanager ” 的提示,则需在当前连接 “属性” 页面中的 “隧道” 选项卡中,取消勾选“转发X11到”这个选项,如下图所示:
    在这里插入图片描述

    1. 如果你的程序不死锁了、线程也不阻塞了、返回结果也拿到了,但是忽然发现,对方输出的内容是纯字符格式,并不能转成 JSON 方便前端使用,那么你可能需要下面这段代码。
    import com.alibaba.fastjson2.JSON;
    import com.alibaba.fastjson2.JSONArray;
    
    public class JsonUtil {
    
        /**
         * 将 [{aaa:123,bbb:456}] 这样的字符串转换为 json 数组
         * @param str 需要转换的字符串
         */
        public static JSONArray str2JSONArray(String str) {
            JSONArray jsonArray = new JSONArray();
            if (str != null) {
                str = str.replace("{", "{\"")
                        .replace(":", "\":\"")
                        .replace(",", "\",\"")
                        .replace("}", "\"}");
                jsonArray = JSON.parseArray(str);
            }
            return jsonArray;
        }
    }
    

    上面的代码是将字符串转换为了 JSON 数组,如果你需要转换成 JSON 对象,则修改 fastjson2 的转换函数即可,这里就不做演示了。

-------------------------------------- 独处的时光里,藏着你未来的样子。 --------------------------------------

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/41297.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Mathematica for Linux v13.1.0 科学计算软件多语言版

Wolfram Mathematica for Linux 中文正式版是一款强大的数学计算科学计算软件&#xff0c;MathWorks MATLAB 和 Wolfram Mathematica 、Maplesoft Maple 并称为三大数学软件&#xff0c;Wolfram Mathematica 中文正式版主要用于符号计算软件&#xff0c;也称为计算机代数系统&a…

MySQL如何恢复不小心误删的数据记录(binlog)

前言 题主于今天&#xff08;2022年11月27日&#xff09; 在线上环境误操作删除了记录&#xff0c;且没有备份数据&#xff0c;通宵排查事故原因&#xff0c;终于没有酿成生产事故。谨以此文记录。 参考资料 https://blog.csdn.net/qq_23543983/article/details/127298578 …

单源最短路径问题(Java)

单源最短路径问题&#xff08;Java&#xff09; 文章目录单源最短路径问题&#xff08;Java&#xff09;1、问题描述2、算法思路3、代码实现4、算法正确性和计算复杂性4.1 贪心选择性质4.2 最优子结构性质4.3 计算复杂性5、参考资料1、问题描述 给定带权有向图G(V,E),其中每条…

分布式电源接入对配电网的影响matlab程序(IEEE9节点系统算例)

分布式电源接入对配电网的影响matlab程序&#xff08;IEEE9节点系统算例&#xff09; 摘 要&#xff1a;分布式电源的接入使得配电系统从放射状无源网络变为分布有中小型电源的有源网络。带来了使单向流动的电流方向具有了不确定性等等问题&#xff0c;使得配电系统的控制和管…

Android反编译apk

文章目录安装Android Studio1. 解压apk文件方法一&#xff1a;使用apktool反编译&#xff08;得到的是.smali文件和可直接读的资源文件&#xff0c;如果要得到.dex文件&#xff0c;还要看方法二&#xff09;方法二&#xff1a;使用解压工具解压&#xff08;得到的是.dex文件和二…

SpringBoot项目集成Dubbo

1.环境搭建 为整合Dubbo之前&#xff0c;我们所写的项目都是单一应用架构&#xff0c;只需要一个应用&#xff0c;将所有功能都部署在一起&#xff0c;在应用内部是控制层调用业务层&#xff0c;业务层调用数据持久层&#xff1b;如今&#xff0c;整合Dubbo后&#xff0c;我们…

独立产品灵感周刊 DecoHack #039 - 制作自己的音乐墙

本周刊记录有趣好玩的独立产品设计开发相关内容&#xff0c;每周发布&#xff0c;往期内容同样精彩&#xff0c;感兴趣的伙伴可以点击订阅我的周刊。为保证每期都能收到&#xff0c;建议邮件订阅。欢迎通过 Twitter 私信推荐或投稿。自荐产品 1. planet-tab - 由独立开发者 ha…

【云原生】Docker的私有仓库部署——Harbor

内容预知 1.Docker原生私有仓库—— Registry 1.1 Registry的简单了解 1.2 Registry的部署过程 步骤一&#xff1a;拉取相关的镜像 步骤二&#xff1a;进行 Registry的相关yml文件配置&#xff08;docker-compose&#xff09; 步骤三&#xff1a;镜像的推送 2. Registry的…

SpringBoot SpringBoot 原理篇 2 自定义starter 2.6 拦截器开发

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 原理篇 文章目录SpringBootSpringBoot 原理篇2 自定义starter2.6 拦截器开发2.6.1 拦截器开发2.6.2 小结2 自定义starter …

2022年11月27日学习 SVM

SVM&#xff0c;英文全称为 Support Vector Machine&#xff0c;中文名为支持向量机 ​ SVM也是一种分类算法&#xff0c;它的核心思想用我自己的话来讲就是先找到两个类别中距离最近的几个点作为支持向量&#xff0c;然后计算超平面&#xff0c;超平面需要间隔最大化。然后用超…

【Hack The Box】linux练习-- Previse

HTB 学习笔记 【Hack The Box】linux练习-- Previse &#x1f525;系列专栏&#xff1a;Hack The Box &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年11月27日&#x1f334; &#x1f…

Microsoft SQL Server中的错误配置

介绍 这篇文章将介绍如何利用Microsoft SQL Server中的错误配置&#xff0c;尝试获取反向shell并熟悉Impacket工具的使用&#xff0c;以便进一步攻击某些服务。 impacket的安装地址&#xff1a;https://github.com/SecureAuthCorp/impacket Impacket是用于处理网络协议的Pyt…

FPGA学习-vivado软件的使用

FPGA学习-vivado软件的使用1.杂谈2. vivado新建工程1.杂谈 又被封了7天。 正好封控前领导让我改下fpga代码&#xff0c;趁这个机会好好学习下&#xff0c;虽然在这块一片空白&#xff0c;但是毕竟这块是我的短板&#xff0c;一个不会写代码的硬件工程师是一个不完整的硬件工程…

无条码商品新建商品档案,搭配蓝牙便携打印机移动打印条码标签

null无条码商品的商品档案新建&#xff0c;并打印条码标签&#xff0c;即可实现仓库条码管理&#xff0c;扫码入库&#xff0c;出库&#xff0c;盘点等操作。, 视频播放量 1、弹幕量 0、点赞数 0、投硬币枚数 0、收藏人数 0、转发人数 0, 视频作者 汉码盘点机PDA, 作者简介 &am…

【WSN通信】A_Star改进LEACH多跳传输协议【含Matlab源码 487期】

⛄一、 A_Star改进LEACH多跳传输协议简介 1 理论基础 1.1 A*算法 A算法是一种智能搜索算法,他在求解问题时所得到的结果会选择所有路径中代价最小的节点。 A算法是一种基于启发式函数的算法,搜索过程如下:首先创建两个分别命名为open表和close表的表格,其中open表中存放还未访…

【Hack The Box】linux练习-- Doctor

HTB 学习笔记 【Hack The Box】linux练习-- Doctor &#x1f525;系列专栏&#xff1a;Hack The Box &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年11月23日&#x1f334; &#x1f3…

量子计算(九):复合系统与联合测量

文章目录 复合系统与联合测量 一、张量积 二、复合系统的状态演化 复合系统与联合测量 拥有两个或两个以上的量子比特的量子系统通常被称为复合系统&#xff08;composite systems&#xff09;。单量子比特系统的描述与测量已有所了解&#xff0c;那么多个量子比特的系统该如…

R语言基于ARMA-GARCH过程的VaR拟合和预测

本文展示了如何基于基础ARMA-GARCH过程&#xff08;当然这也涉及广义上的QRM&#xff09;来拟合和预测风险价值&#xff08;Value-at-Risk&#xff0c;VaR&#xff09;。 最近我们被客户要求撰写关于ARMA-GARCH的研究报告&#xff0c;包括一些图形和统计输出。 视频&#xff…

树莓派安装ubuntu系统

ubuntu镜像下载 官方地址&#xff1a;https://cn.ubuntu.com/download/raspberry-pi 清华镜像&#xff1a;https://mirrors.tuna.tsinghua.edu.cn/ubuntu-cdimage/ubuntu/releases/22.04.1/release/ 准备树莓派镜像工具&#xff08;Raspberry Pi Imager&#xff09; 下载地…

如何在 Spring Boot 中使用 JPA 和 JPQL 进行自定义查询示例

在本教程中&#xff0c;您将了解如何在 Spring Boot 示例中使用 Spring JPA Query进行自定义查询。我将向您展示&#xff1a; 使用JPQL&#xff08;Java持久性查询语言&#xff09;的方法如何在 Spring 引导中执行 SQL 查询具有 WHERE 条件的 JPA 选择查询示例内容 JPQL 与本机…