首页 > 系统相关 >Java调用Linux命令行SpringBoot

Java调用Linux命令行SpringBoot

时间:2024-08-06 08:59:26浏览次数:11  
标签:Java String import cmd res private Linux new SpringBoot

原文链接:https://blog.csdn.net/u014628771/article/details/108308337

需求
在Linux服务器上的SpringBoot程序中,调用git clone,之后遍历git仓库中的所有文件。

遍历git仓库可以使用File类实现,现在的问题是需要在SpringBoot程序中调用git clone命令。

实现方式
使用Java native的Process类和ProcessBuilder类来执行命令行。

话不多说,直接上代码(解释都在注解中)

关键代码
注意,这段代码实际上有几个坑,不建议直接复制使用(优化后的代码在文章末尾,建议直接复制使用),具体请看下文

private String executeGitClone() {
Process p = null;
// git clone命令
String cmd = "git clone https://{username}:{pwd}@github.com/{group}/{repo}.git";
try {
// 起子进程执行cmd命令
ProcessBuilder pb = new ProcessBuilder(cmd);
p = pb.start();
// 等待命令执行结束
int exitValue = p.waitFor();
// 创建readers, resReader用于读取标准输出,errReader用于读取错误输出
BufferedReader resReader = new BufferedReader(new InputStreamReader((p.getInputStream())));
BufferedReader errReader = new BufferedReader(new InputStreamReader((p.getErrorStream())));

StringBuilder resStringBuilder = new StringBuilder();
StringBuilder errStringBuilder = new StringBuilder();
String line;
while ((line = resReader.readLine()) != null) {
resStringBuilder.append(line);
}
while ((line = errReader.readLine()) != null) {
errStringBuilder.append(line);
}

// linux标准, exitValue为0时,表示执行正确结束
// 当exitValue > 0时,抛出异常,并将获取的错误信息包装在Exception中
if (exitValue > 0) {
throw new RuntimeException(errStringBuilder.toString());
}

// 返回标准输出
return resStringBuilder.toString();
} catch (Exception e) {
throw new CodeSearchException(e, SYSTEM_COMMAND_ERROR, e.getMessage());
} finally {
// 销毁子进程,释放资源
if (p != null) {p.destroy();}
}
}
需要注意的坑&问题
进程阻塞
代码中的顺序是,先执行命令,等命令执行结束后。

再读取标准输出流和错误输出流中的内容。

但是,标准输出流和错误输出流其实是先输出到缓冲区,当缓冲区写满时,进程会被挂起。

所以实际上,我们应该在执行pb.start()之后就启动两个线程,异步地读取标准输入流和错误输入流中的内容。

执行参数无效
执行进程还有更源生的方式,那就是使用Runtime.getRuntime().exec()的方式来执行,

ProcessBuilder实际上是封装过的类,如果使用Runtime来执行的话,给命令传多个参数会有问题。

使用ProcessBuilder,并将参数置于入参数组中即可。

不够优雅:起两个线程来读取返回结果(或者是上述代码中启动两个while循环),代码看起来不太优雅
改善&&结合SpringBoot
我们将“执行命令行”封装成一个Spring中的service,入参是命令,当执行顺利时返回标准输入流中的结果,当执行失败时抛出异常。

并使用线程池来用线程读取流中的数据。

// 定义Command Srevice接口
public interface CommandService {
String executeCmd(String cmd);
}
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
* @version : CommandServiceImpl.java, v 0.1 2020年08月05日 11:38 AM Exp $
*/
@Service
public class CommandServiceImpl implements CommandService, InitializingBean {
@Value("${cmd.threadname:cmd-executor}")
private String threadName;

@Value("${cmd.taskQueueMaxStorage:20}")
private Integer taskQueueMaxStorage;

@Value("${cmd.corePoolSize:4}")
private Integer corePoolSize;

@Value("${cmd.maximumPoolSize:8}")
private Integer maximumPoolSize;

@Value("${cmd.keepAliveSeconds:15}")
private Integer keepAliveSeconds;
private ThreadPoolExecutor executor;
private static final String BASH = "sh";
private static final String BASH_PARAM = "-c";

// use thread pool to read streams
@Override
public void afterPropertiesSet() {
executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveSeconds, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(taskQueueMaxStorage),
new ThreadFactory() {
public Thread newThread(Runnable r) {
return new Thread(r, threadName + r.hashCode());
}
},
new ThreadPoolExecutor.AbortPolicy());
}

@Override
public String executeCmd(String cmd) {
Process p = null;
String res;
try {
// need to pass command as bash's param,
// so that we can compatible with commands: "echo a >> b.txt" or "bash a && bash b"
List<String> cmds = new ArrayList<>();
cmds.add(BASH);
cmds.add(BASH_PARAM);
cmds.add(cmd);
ProcessBuilder pb = new ProcessBuilder(cmds);
p = pb.start();

Future<String> errorFuture = executor.submit(new ReadTask(p.getErrorStream()));
Future<String> resFuture = executor.submit(new ReadTask(p.getInputStream()));
int exitValue = p.waitFor();
if (exitValue > 0) {
throw new RuntimeException(errorFuture.get());
}
res = resFuture.get();

} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (p != null) {p.destroy();}
}
// remove System.lineSeparator() (actually it's '\n') in the end of res if exists
if (StringUtils.isNotBlank(res) && res.endsWith(System.lineSeparator())) {
res = res.substring(0, res.lastIndexOf(System.lineSeparator()));
}
return res;
}

class ReadTask implements Callable<String> {
InputStream is;

ReadTask(InputStream is) {
this.is = is;
}

@Override
public String call() throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
return sb.toString();
}
}
}


标签:Java,String,import,cmd,res,private,Linux,new,SpringBoot
From: https://www.cnblogs.com/fswhq/p/17686642.html

相关文章

  • 基于Java中的SSM框架实现在线音乐网站系统项目【项目源码+论文说明】
    基于Java中的SSM框架实现在线音乐网站系统演示摘要本文讲述了使用JSP语言及HTML5语言及MySql数据库技术开发的音乐网站的设计与实现。本文所讲的JSP音乐系统是通过所学的知识创办一个类似于QQ音乐或者酷狗音乐性质的网站平台,使所有对国内外音乐欣赏感兴趣的人都可以不必再......
  • Apache Flink开发时选择Java还是Scala作为编程语言
    在ApacheFlink的开发过程中,选择Java还是Scala作为编程语言是一个重要的决策点。这两种语言各有其独特的优势和特点,适合不同的开发场景和需求。以下是对这一选择的详细探讨,旨在帮助开发者更好地理解并做出合理的选择。一、ApacheFlink简介ApacheFlink是一个开源的分布式......
  • 【Java基础】03选择结构
    if分支ifif(条件){代码块;}if...else...if(条件){代码块1;}else{代码块2;}if...elseif...else...if(条件1){代码块1;}elseif(条件2){代码块2;//elseif可以写多个}else{代码块3;//else可以省略不写}if嵌套if(条件1){......
  • 【TS】 TypeScript声明文件:打通JavaScript和TypeScript的桥梁
     TypeScript声明文件的讲解: TypeScript声明文件(DeclarationFile)在TypeScript项目中具有举足轻重的地位,它是连接TypeScript严格的类型系统与外部无类型或类型不明确的JavaScript代码的关键纽带。 声明文件的核心价值在于为TypeScript编译器提供必要的类型信息......
  • 【Java数据结构】---初始数据结构
    乐观学习,乐观生活,才能不断前进啊!!!我的主页:optimistic_chen我的专栏:c语言,Java欢迎大家访问~创作不易,大佬们点赞鼓励下吧~前言从今天开始我们就要学习Java的数据据结构部分,根据前面Java语法的基础上,更加深入的了解算法的基本知识。文章目录前言什么是数据结......
  • Linux网络编程2
    TCP编程顺序图socket()函数socket()函数用于创建一个新的套接字。它是进行网络编程的第一步,因为所有的网络通信都需要通过套接字来进行。原型:#include<sys/socket.h> intsocket(intdomain,inttype,intprotocol);   domain:指定协议族,对于TCP/IP网络,它通常......
  • 基于SpringBoot+Vue+uniapp的信息技术知识赛系统的详细设计和实现(源码+lw+部署文档+
    文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaitsPlus系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......
  • 基于SpringBoot+Vue+uniapp的政府管理的系统设计的详细设计和实现(源码+lw+部署文档+
    文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaitsPlus系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......
  • 基于SpringBoot+Vue+uniapp的学生网上选课系统的详细设计和实现(源码+lw+部署文档+讲
    文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaitsPlus系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......
  • linux运维一天一个shell命令之tcpdump详解
    一、tcpdump的概念tcpdump是一个数据包捕获工具,能够拦截和显示通过网络接口的数据包。它可以实时捕获数据包,也可以将捕获的数据保存到文件中以便后续分析。tcpdump支持基于多种条件(如IP地址、端口号、协议等)来捕获特定的数据包。二、主要功能和特点1.数据包捕获:tcp......