这篇文章主要介绍了java调用shell脚本及注意事项说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
需求
get方法下载远程zip包,然后zip包解压,取出第一级目录再次进行压缩获取新的压缩zip包。
问题
如果选择使用java代码的IO流操作,在不确定zip包大小的情况下可能会占用很大的内存,所以选择异步调用shell脚本来实现这个操作;
介绍
1、通过ProcessBuilder进行调度
//解决脚本没有执行权限
ProcessBuilder builder = new ProcessBuilder("/bin/chmod", "755", scriptPath);
Process process = builder.start();
process.waitFor();
2、直接通过系统的Runtime类执行shell
Runtime类封装了运行时的环境。每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。
一般不能实例化一个Runtime对象,应用程序也不能创建自己的 Runtime 类实例,但可以通过 getRuntime 方法获取当前Runtime运行时对象的引用。
一旦得到了一个当前的Runtime对象的引用,就可以调用Runtime对象的方法去控制Java虚拟机的状态和行为。
//SHELL_FILE_DIR + RUNNING_SHELL_FILE为脚本的全路径,后面传递给shell脚本多个参数用空格分隔
Sting cmd = SHELL_FILE_DIR + RUNNING_SHELL_FILE + " "+param1+" "+param2+" "+param3
//RunTime执行脚本
Process ps = Runtime.getRuntime().exec(cmd);
//waitFor等待shell运行完,返回值如果为0,则表明正常运行完
int execStatus = ps.waitFor();
遇到的问题
1、没权限运行
通过ProcessBuilder来设置文件的权限
//解决脚本没有执行权限,scriptPath为脚本全路径
ProcessBuilder builder = new ProcessBuilder("/bin/chmod", "755", scriptPath);
Process process = builder.start();
process.waitFor();
2、调用shell脚本提示:No such file or directory
原因:文件格式不正确导致,在windows下编写的sh文件,文件是DOS格式。使用:set ff=unix 强制将文件转换为unix格式。
具体操作:
vim模式打开这个shell脚本,查看编码格式后设置成unix编码,输入:set ff?,查看格式是否是fileformat=unix;如果不是,设置成unix
:set ff=unix后保存(:wq)即可。
3、shell脚本输出太大,程序卡死问题
Java在执行Runtime.getRuntime().exec(command)之后,Linux会创建一个进程,该进程与JVM进程建立三个管道连接,标准输入流、标准输出流、标准错误流。
当标准输出流或标准错误流非常庞大的时候,会出现调用waitFor方法卡死的bug。真实的环境中,当标准输出在10000行左右的时候,就会出现卡死的情况。
原因分析:
假设linux进程不断向标准输出流和标准错误流写数据,而JVM却不读取,数据会暂存在linux缓存区,当缓存区存满之后导致该进程无法继续写数据,会僵死,导致java进程会卡死在waitFor()处,永远无法结束。
解决方式:
由于标准输出和错误输出都会向Linux缓存区写数据,而脚本如何输出这两种流是Java端不能确定的。为了不让shell脚本的子进程卡死,这两种输出需要分别读取,而且不能互相影响。所以必须新开两个线程来进行读取。
new Thread() {
public void run() {
BufferedReader br1 = new BufferedReader(new InputStreamReader(is1));
try {
String line1 = null;
while ((line1 = br1.readLine()) != null) {
if (line1 != null){}
}
} catch (IOException e) {
e.printStackTrace();
}
finally{
try {
is1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
BufferedReader br2 = new BufferedReader(new InputStreamReader(is2));
try {
String line2 = null ;
while ((line2 = br2.readLine()) != null ) {
if (line2 != null){}
}
} catch (IOException e) {
e.printStackTrace();
}
finally{
try {
is2.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
下面提供工具类和自己的shell脚本
工具类
import org.apache.commons.lang.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.Arrays;
public class ShellCommandUtils {
/*
* 日志
*/
private final static Logger logger = LoggerFactory.getLogger(ShellCommandUtils.class);
/**
* @Description: 执行shell
*
* @param scriptPath :脚本路径
* @param param 脚本参数
* @Return: void
* @Date: 2019/3/22
*/
public static int execShell(String scriptPath, String... param) {
logger.info("调用处理压缩包的shell脚本,params=" + param.toString());
Arrays.stream(param).forEach(item-> logger.info(item));
//执行结果
int result = 1;
try {
String[] cmd = new String[]{scriptPath};
//为了解决参数中包含空格
cmd = (String[]) ArrayUtils.addAll(cmd, param);
logger.info("调用处理压缩包的shell脚本,cmd=" + cmd.toString());
Arrays.stream(cmd).forEach(item-> logger.info(item));
logger.info("解决脚本没有执行权限逻辑start");
//解决脚本没有执行权限
ProcessBuilder builder = new ProcessBuilder("/bin/chmod", "755", scriptPath);
Process process = builder.start();
process.waitFor();
logger.info("解决脚本没有执行权限逻辑end");
logger.info("开始执行runtime的脚本start");
Process ps = Runtime.getRuntime().exec(cmd);
logger.info("把缓冲区读出来打log start");
//处理InputStream的线程,获取进程的标准输入流
final InputStream is1 = ps.getInputStream();
//获取进城的错误流
final InputStream is2 = ps.getErrorStream();
//启动两个线程,一个线程负责读标准输出流,另一个负责读标准错误流
new Thread() {
public void run() {
BufferedReader br1 = new BufferedReader(new InputStreamReader(is1));
try {
String line1 = null;
while ((line1 = br1.readLine()) != null) {
if (line1 != null){}
}
} catch (IOException e) {
e.printStackTrace();
}
finally{
try {
is1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
new Thread() {
public void run() {
BufferedReader br2 = new BufferedReader(new InputStreamReader(is2));
try {
String line2 = null ;
while ((line2 = br2.readLine()) != null ) {
if (line2 != null){}
}
} catch (IOException e) {
e.printStackTrace();
}
finally{
try {
is2.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
//等待shell脚本结果
int execStatus = ps.waitFor();
logger.info("执行runtime的脚本end");
logger.info("shell脚本执行结果--execStatus ="+execStatus);
result = execStatus;
logger.info("返回值为result=" + result);
} catch (Exception e) {
logger.error("调用处理压缩包的shell出现异常!", e);
}
return result;
}
}
shell脚本
#!/bin/sh
#处理压缩包
fileName=$1
url=$2
homePath=$3
#开始处理数据逻辑
echo fileName=$fileName, url=$url, homePath=$homePath
#判断参数不为空
if [ -n "$fileName" ]; then
if [ -n "$url" ]; then
#0.cd到对应目录
cd $homePath
#1.调用get方法获取zip包并下载到本地
wget -O $fileName.zip $url
#2.解压zip包
unzip $fileName.zip
#3.删除zip包
rm -f $fileName.zip
#4.进入解压完的文件夹
cd $fileName
#5.压缩当前文件夹下所有文件为指定文件名的zip
zip -r ../$fileName.zip ./*
#6.删除之前解压的文件夹
rm -rf $homePath$fileName
fi
fi
echo "deal package end"
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程学习网。
本文标题为:java调用shell脚本及注意事项说明
基础教程推荐
- Java数据结构之对象比较详解 2023-03-07
- Java并发编程进阶之线程控制篇 2023-03-07
- Java实现线程插队的示例代码 2022-09-03
- java实现多人聊天系统 2023-05-19
- Java实现查找文件和替换文件内容 2023-04-06
- java基础知识之FileInputStream流的使用 2023-08-11
- ConditionalOnProperty配置swagger不生效问题及解决 2023-01-02
- JDK数组阻塞队列源码深入分析总结 2023-04-18
- springboot自定义starter方法及注解实例 2023-03-31
- Java文件管理操作的知识点整理 2023-05-19