文章目录
- 远程连接服务器
- 执行linux命令或shell脚本
- 介绍
- Process的方法
- 相关类UML
- 工具类
- 基本工具类
- 依赖第三方的工具类
远程连接服务器
java程序远程linux服务器有两个框架分别是:jsch与ganymed-ssh2框架。推荐使用jsch框架,因为ganymed-ssh2框架不支持麒麟服务器的连接,原因是openssl版本过高,ganymed-ssh框架不维护了所以不支持。
import cn.hutool.core.text.StrSplitter;
import cn.hutool.core.util.StrUtil;
import com.jcraft.jsch.*;
import java.io.*;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Properties;
/**
* 远程连接linux服务并执行命令
* 网上查询经过验证可以使用
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.21</version>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.55</version>
</dependency>
*/
public class ShellUtils {
private static Session session;
private ShellUtils() {}
public static ShellUtils getInstance() {
return new ShellUtils();
}
/**
* 初始化
* @throws JSchException JSch异常
*/
public void init(String ip, Integer port, String username,
String password) throws JSchException {
JSch jsch = new JSch();
jsch.getSession(username, ip, port);
session = jsch.getSession(username, ip, port);
session.setPassword(password);
Properties sshConfig = new Properties();
sshConfig.put("StrictHostKeyChecking", "no");
session.setConfig(sshConfig);
session.connect(1200 * 1000);
}
// 执行一条命令
public String execCmd(String command) throws Exception {
// 打开执行shell指令的通道
Channel channel = session.openChannel("exec");
ChannelExec channelExec = (ChannelExec) channel;
String cmd = "source /etc/profile && source ~/.bash_profile " +
"&& source ~/.bashrc && adb devices";
channelExec.setCommand(cmd);
channelExec.setCommand(command);
channel.setInputStream(null);
channelExec.setErrStream(System.err);
// channel.setXForwarding();
channel.connect();
StringBuilder sb = new StringBuilder(16);
try (InputStream in = channelExec.getInputStream();
InputStreamReader isr = new InputStreamReader(in,
StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(isr)) {
String buffer;
while ((buffer = reader.readLine()) != null) {
sb.append(buffer);
}
} finally {
if (channelExec != null && channelExec.isConnected()) {
channelExec.disconnect();
}
if (channel != null && channel.isConnected()) {
channel.disconnect();
}
}
return sb.toString();
}
// 执行一条命令 获取错误流中的内容
public String execCmdErrContent(String command) throws Exception {
// 打开执行shell指令的通道
Channel channel = session.openChannel("exec");
ChannelExec channelExec = (ChannelExec) channel;
channelExec.setCommand(command);
channel.setInputStream(null);
ByteArrayOutputStream err = new ByteArrayOutputStream();
channelExec.setErrStream(err);
channel.connect();
StringBuilder sb = new StringBuilder(16);
try (InputStream in = channelExec.getErrStream();
InputStreamReader isr = new InputStreamReader(in,
StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(isr)) {
String buffer;
while ((buffer = reader.readLine()) != null) {
sb.append("\n").append(buffer);
}
if (StrUtil.contains(sb.toString(), "没有那个文件或目录")) {
return "";
} else {
return sb.toString();
}
} finally {
if (channelExec != null && channelExec.isConnected()) {
channelExec.disconnect();
}
if (channel != null && channel.isConnected()) {
channel.disconnect();
}
}
}
public static void closeConnect() {
if (session != null && session.isConnected()) {
session.disconnect();
}
}
public static void main(String[] args) {
ShellUtils instance = ShellUtils.getInstance();
try {
instance.init("192.168.81.122", 22, "root", "test1");
String cmd = "ps -ef | grep monitor-0.0.1-SNAPSHOT.jar " +
"| grep -v xjar|grep -v grep | awk '{print $2}'";
String ls = instance.execCmd(cmd);
// String cmd = "cat /opt/dmdbms/log/dm_DW1_01B_202203.log " +
// "| grep -v 'INFO'";
// String ls = instance.execCmd(cmd);
// List<String> lineFreedList = StrSplitter.splitByRegex(
// StrUtil.trimToEmpty(ls),
// "\n", -1, true, true
// );
// for (String s : lineFreedList) {
// List<String> stringList = StrSplitter.split(
// StrUtil.trimToEmpty(s),
// "=", -1, true, true
// );
// System.out.println(stringList);
// }
System.out.println(ls);
// // 计算内存使用率(已使用内存/总内存)
// String cmd1 = "free | sed -n '2p'";
// String freeStr = instance.execCmd(cmd1);
// List<String> freeInfoList = StrSplitter.splitByRegex(
// StrUtil.trimToEmpty(freeStr),
// "\\s+", -1, true, true
// );
// String allMemory = freeInfoList.get(1);
// String usedMemory = freeInfoList.get(2);
// double f1 = new BigDecimal(
// Float.valueOf(usedMemory) / Float.valueOf(allMemory)
// )
// .setScale(2, BigDecimal.ROUND_HALF_UP)
// .doubleValue() * 100;
// System.out.println(ls);
} catch (Exception e) {
System.out.println("error info");
e.printStackTrace();
} finally {
ShellUtils.closeConnect();
}
}
}
执行linux命令或shell脚本
在Java中,调用runtime线程执行脚本是非常消耗资源的,所以切记不要频繁使用!
在调用runtime去执行脚本的时候,其实就是JVM开了一个子线程去调用JVM所在系统的命令,其中开了三个通道:输入流、输出流、错误流,其中输出流就是子线程走调用的通道。
大家都知道,waitFor是等待子线程执行命令结束后才访问,但是在runtime中,打开程序的命令如果不关闭,就不算子线程结束,比如如下代码。
private static Process p = null;
p = Runtime.getRuntime().exec("notepad.exe");
p.waitFor();
System.out.println("---------------我被执行了");
以上代码中,打开windows中记事本,如果我们不手动关闭记事本,那么输出语句就不会执行,这点是需要理解的。
介绍
// 在单独的进程中执行指定的字符串命令
public Process exec(String command)
// 在单独的进程中执行指定命令和变量
public Process exec(String[] cmdArray)
// 在指定环境的独立进程中执行指定命令和变量
public Process exec(String command,String[] envp)
// 在指定环境的独立进程中执行指定命令和变量
public Process exec(String[] cmdArray,String[] envp)
// 在有指定的环境和工作目录的独立进程中执行指定的字符串命令
public Process exec(String command,String[] encp,File dir)
// 在指定环境和工作目录的独立进程中执行指定的命令和变量
public Process exec(String[] cmdarray,String[] envp,File dir)
- cmdArray: 包含所调用命令及其参数的数组。
- 如果把命令放到一个String[]中时,必须把命令中每个部分作为一个元素存在String[]中,或者是把命令按照空格符分割得到的String[]。
//right
String[] cmdArray = {"tar", "-cf", tarName, fileName};
//error
String[] cmdArray = {"tar -cf", tarName, fileName};
- command: 一条指定的系统命令。
- envp: 字符串数组,其中每个元素的环境变量的设置格式为name=value;如果子进程应该继承当前进程的环境,则该参数为 null。
- 模拟环境变量
val=2
call=Bash Shell
- 测试shell脚本
#!/usr/bin/env bash
args=1
if [ $# -eq 1 ];then
args=$1
echo "The argument is: $args"
fi
echo "This is a $call"
start=`date +%s`
sleep 3s
end=`date +%s`
cost=$((($end - $start) * $args * $val))
echo "Cost Time: $cost"
- 调用
String cmd = "sh " + script + " " + args;
String[] evnp = {"val=2", "call=Bash Shell"};
process = Runtime.getRuntime().exec(cmd, evnp);
BufferedReader input = new BufferedReader(
new InputStreamReader(process.getInputStream())
);
String line = "";
while ((line = input.readLine()) != null) {
System.out.println(line);
}
input.close();
- 返回结果
/root/experiment/
The argument is: 4
This is a Bash Shell
Cost Time: 24
- dir: 子进程的工作目录;如果子进程应该继承当前进程的工作目录,则该参数为 null。
Process的方法
- destroy():杀掉子进程
- exitValue():返回子进程的出口值,值0表示正常终止
- getErrorStream():获取子进程的错误流
- getInputStream():获取子进程的输入流
- getOutputStream():获取子进程的输出流
- waitFor():导致当前线程等待,如有必要,一直要等到由该Process对象表示的进程已经终止。如果已终止该子进程,此方法立即返回。如果没有终止该子进程,调用的线程将被阻塞,直到退出子进程,0表示正常终止。
相关类UML
java.lang.Runtime
java.lang.Process
工具类
基本工具类
package utils;
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class ShellUtils {
//执行shell命令并获取输出结果
public static String execAndResult(String cmd){
System.out.println("cmd: " + cmd);
String result = "";
try {
Runtime runtime = Runtime.getRuntime();
Process ps = runtime.exec(cmd);
//获取执行结果的输入
BufferedReader br = new BufferedReader(
new InputStreamReader(ps.getInputStream())
);
StringBuffer sb = new StringBuffer();
String line;
while ((line = br.readLine()) != null) {
//执行结果加上回车
sb.append(line);
}
result = sb.toString();
br.close();
System.out.println("waitFor:" + ps.waitFor());
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
public static void main(String[] args) {
//执行普通命令
execAndResult("shutdown -r");
//执行脚本
execAndResult("sh /data/jqxt/script/bak_pz.sh");
}
}
依赖第三方的工具类
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.StaticLog;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.management.ManagementFactory;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
/**
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.21</version>
</dependency>
*/
public class MyRuntimeUtil {
/**
* 执行系统命令,使用系统默认编码
*
* @param cmds 命令列表,每个元素代表一条命令
* @return 执行结果
* @throws IORuntimeException IO异常
*/
public static String execForStr(String... cmds)
throws IORuntimeException {
return execForStr(CharsetUtil.systemCharset(), cmds);
}
/**
* 执行系统命令,使用系统默认编码
*
* @param charset 编码
* @param cmds 命令列表,每个元素代表一条命令
* @return 执行结果
* @throws IORuntimeException IO异常
* @since 3.1.2
*/
public static String execForStr(Charset charset, String... cmds)
throws IORuntimeException {
return getResult(exec(cmds), charset);
}
public static String execForErrStr(Charset charset, String cmd)
throws IORuntimeException {
return getResultErr(exec(cmd), charset);
}
/**
* 执行系统命令,使用系统默认编码
*/
public static String execForStr(Charset charset, String cmds)
throws IORuntimeException {
return getResult(exec(cmds), charset);
}
/**
* 执行系统命令,使用系统默认编码
*
* @param cmds 命令列表,每个元素代表一条命令
* @return 执行结果,按行区分
* @throws IORuntimeException IO异常
*/
public static List<String> execForLines(String... cmds)
throws IORuntimeException {
return execForLines(CharsetUtil.systemCharset(), cmds);
}
/**
* 执行系统命令,使用系统默认编码
*
* @param charset 编码
* @param cmds 命令列表,每个元素代表一条命令
* @return 执行结果,按行区分
* @throws IORuntimeException IO异常
* @since 3.1.2
*/
public static List<String> execForLines(Charset charset, String... cmds)
throws IORuntimeException {
return getResultLines(exec(cmds), charset);
}
/**
* 执行命令<br>
* 命令带参数时参数可作为其中一个参数,也可以将命令和参数组合为一个字符串传入
*
* @param cmds 命令
* @return {@link Process}
*/
public static Process exec(String... cmds) {
Process process;
try {
process = new ProcessBuilder(handleCmds(cmds))
.redirectErrorStream(true)
.start();
} catch (IOException e) {
throw new IORuntimeException(e);
}
return process;
}
/**
* 执行命令<br>
* 命令带参数时参数可作为其中一个参数,也可以将命令和参数组合为一个字符串传入
*
* @param cmd 命令
* @return {@link Process}
*/
public static Process exec(String cmd) {
Process process;
try {
// 得到Java进程的相关Runtime运行对象
Runtime runtime = Runtime.getRuntime();
if (cmd.indexOf("|") > 0) {
String[] cmdArr = {"sh", "-c", cmd};
process = runtime.exec(cmdArr);
} else {
process = runtime.exec(cmd);
}
} catch (IOException e) {
throw new IORuntimeException(e);
}
return process;
}
/**
* 执行命令<br>
* 命令带参数时参数可作为其中一个参数,也可以将命令和参数组合为一个字符串传入
*
* @param envp 环境变量参数,传入形式为key=value,null表示继承系统环境变量
* @param cmds 命令
* @return {@link Process}
* @since 4.1.6
*/
public static Process exec(String[] envp, String... cmds) {
return exec(envp, null, cmds);
}
/**
* 执行命令<br>
* 命令带参数时参数可作为其中一个参数,也可以将命令和参数组合为一个字符串传入
*
* @param envp 环境变量参数,传入形式为key=value,null表示继承系统环境变量
* @param dir 执行命令所在目录(用于相对路径命令执行),null表示使用当前进程执行的目录
* @param cmds 命令
* @return {@link Process}
* @since 4.1.6
*/
public static Process exec(String[] envp, File dir, String... cmds) {
try {
return Runtime.getRuntime().exec(handleCmds(cmds), envp, dir);
} catch (IOException e) {
throw new IORuntimeException(e);
}
}
// ------------------------------------ result
/**
* 获取命令执行结果,使用系统默认编码,获取后销毁进程
*
* @param process {@link Process} 进程
* @return 命令执行结果列表
*/
public static List<String> getResultLines(Process process) {
return getResultLines(process, CharsetUtil.systemCharset());
}
/**
* 获取命令执行结果,使用系统默认编码,获取后销毁进程
*
* @param process {@link Process} 进程
* @param charset 编码
* @return 命令执行结果列表
* @since 3.1.2
*/
public static List<String> getResultLines(Process process, Charset charset) {
InputStream in = null;
try {
in = process.getInputStream();
return IoUtil.readLines(in, charset, new ArrayList<>());
} finally {
IoUtil.close(in);
destroy(process);
}
}
/**
* 获取命令执行结果,使用系统默认编码,,获取后销毁进程
*
* @param process {@link Process} 进程
* @return 命令执行结果列表
* @since 3.1.2
*/
public static String getResult(Process process) {
return getResult(process, CharsetUtil.systemCharset());
}
/**
* 获取命令执行结果,获取后销毁进程
*
* @param process {@link Process} 进程
* @param charset 编码
* @return 命令执行结果列表
* @since 3.1.2
*/
public static String getResult(Process process, Charset charset) {
InputStream in = null;
InputStream errorStream = null;
try {
in = process.getInputStream();
errorStream = process.getErrorStream();
String errorResult = IoUtil.read(errorStream, charset);
if (StrUtil.isNotBlank(errorResult)) {
StaticLog.warn("Shell command execution error, because {}", errorResult);
}
return IoUtil.read(in, charset);
} finally {
IoUtil.close(in);
IoUtil.close(errorStream);
destroy(process);
}
}
/**
* 获取错误的执行结果,获取后销毁进程
*
* @param process {@link Process} 进程
* @param charset 编码
* @return 命令执行结果列表
* @since 3.1.2
*/
public static String getResultErr(Process process, Charset charset) {
InputStream in = null;
InputStream errorStream = null;
try {
in = process.getInputStream();
errorStream = process.getErrorStream();
// System.out.println("252"+IoUtil.read(errorStream, charset));
return IoUtil.read(errorStream, charset);
} finally {
IoUtil.close(in);
IoUtil.close(errorStream);
destroy(process);
}
}
/**
* 获取命令执行异常结果,使用系统默认编码,,获取后销毁进程
*
* @param process {@link Process} 进程
* @return 命令执行结果列表
* @since 4.1.21
*/
public static String getErrorResult(Process process) {
return getErrorResult(process, CharsetUtil.systemCharset());
}
/**
* 获取命令执行异常结果,获取后销毁进程
*
* @param process {@link Process} 进程
* @param charset 编码
* @return 命令执行结果列表
* @since 4.1.21
*/
public static String getErrorResult(Process process, Charset charset) {
InputStream in = null;
try {
in = process.getErrorStream();
return IoUtil.read(in, charset);
} finally {
IoUtil.close(in);
destroy(process);
}
}
/**
* 销毁进程
*
* @param process 进程
* @since 3.1.2
*/
public static void destroy(Process process) {
if (null != process) {
process.destroy();
}
}
/**
* 增加一个JVM关闭后的钩子,用于在JVM关闭时执行某些操作
*
* @param hook 钩子
* @since 4.0.5
*/
public static void addShutdownHook(Runnable hook) {
Runtime.getRuntime()
.addShutdownHook(
(hook instanceof Thread) ?
(Thread) hook :
new Thread(hook)
);
}
/**
* 获得JVM可用的处理器数量(一般为CPU核心数)
*
* @return 可用的处理器数量
* @since 5.3.0
*/
public static int getProcessorCount() {
return Runtime.getRuntime().availableProcessors();
}
/**
* 获得JVM中剩余的内存数,单位byte
*
* @return JVM中剩余的内存数,单位byte
* @since 5.3.0
*/
public static long getFreeMemory() {
return Runtime.getRuntime().freeMemory();
}
/**
* 获得JVM已经从系统中获取到的总共的内存数,单位byte
*
* @return JVM中剩余的内存数,单位byte
* @since 5.3.0
*/
public static long getTotalMemory() {
return Runtime.getRuntime().totalMemory();
}
/**
* 获得JVM中可以从系统中获取的最大的内存数,单位byte,以-Xmx参数为准
*
* @return JVM中剩余的内存数,单位byte
* @since 5.3.0
*/
public static long getMaxMemory() {
return Runtime.getRuntime().maxMemory();
}
/**
* 获得JVM最大可用内存,计算方法为:<br>
* 最大内存-总内存+剩余内存
*
* @return 最大可用内存
*/
public static long getUsableMemory() {
return getMaxMemory() - getTotalMemory() + getFreeMemory();
}
/**
* 获取当前进程ID,首先获取进程名称,读取@前的ID值,如果不存在,则读取进程名的hash值
*
* @return 进程ID
* @throws UtilException 进程名称为空
* @since 5.7.3
*/
public static int getPid() throws UtilException {
final String processName = ManagementFactory.getRuntimeMXBean()
.getName();
if (StrUtil.isBlank(processName)) {
throw new UtilException("Process name is blank!");
}
final int atIndex = processName.indexOf('@');
if (atIndex > 0) {
return Integer.parseInt(processName.substring(0, atIndex));
} else {
return processName.hashCode();
}
}
/**
* 处理命令,多行命令原样返回,单行命令拆分处理
*
* @param cmds 命令
* @return 处理后的命令
*/
private static String[] handleCmds(String... cmds) {
if (ArrayUtil.isEmpty(cmds)) {
throw new NullPointerException("Command is empty !");
}
// 单条命令的情况
if (1 == cmds.length) {
final String cmd = cmds[0];
if (StrUtil.isBlank(cmd)) {
throw new NullPointerException("Command is blank !");
}
cmds = cmdSplit(cmd);
}
return cmds;
}
/**
* 命令分割,使用空格分割,考虑双引号和单引号的情况
*
* @param cmd 命令,如 git commit -m 'test commit'
* @return 分割后的命令
*/
private static String[] cmdSplit(String cmd) {
final List<String> cmds = new ArrayList<>();
final int length = cmd.length();
final Stack<Character> stack = new Stack<>();
boolean inWrap = false;
final StrBuilder cache = StrUtil.strBuilder();
char c;
for (int i = 0; i < length; i++) {
c = cmd.charAt(i);
switch (c) {
case CharUtil.SINGLE_QUOTE:
case CharUtil.DOUBLE_QUOTES:
if (inWrap) {
if (c == stack.peek()) {
//结束包装
stack.pop();
inWrap = false;
}
cache.append(c);
} else {
stack.push(c);
cache.append(c);
inWrap = true;
}
break;
case CharUtil.SPACE:
if (inWrap) {
// 处于包装内
cache.append(c);
} else {
cmds.add(cache.toString());
cache.reset();
}
break;
default:
cache.append(c);
break;
}
}
if (cache.hasContent()) {
cmds.add(cache.toString());
}
return cmds.toArray(new String[0]);
}
}