博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
java调用shell脚本及注意事项
阅读量:4027 次
发布时间:2019-05-24

本文共 6466 字,大约阅读时间需要 21 分钟。

需求:

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=$1url=$2homePath=$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	fifiecho "deal package end"

 

转载地址:http://evtbi.baihongyu.com/

你可能感兴趣的文章
IDEA Properties中文unicode转码问题
查看>>
Idea下安装Lombok插件
查看>>
zookeeper
查看>>
Idea导入的工程看不到src等代码
查看>>
技术栈
查看>>
Jenkins中shell-script执行报错sh: line 2: npm: command not found
查看>>
8.X版本的node打包时,gulp命令报错 require.extensions.hasownproperty
查看>>
Jenkins 启动命令
查看>>
Maven项目版本继承 – 我必须指定父版本?
查看>>
Maven跳过单元测试的两种方式
查看>>
通过C++反射实现C++与任意脚本(lua、js等)的交互(二)
查看>>
利用清华镜像站解决pip超时问题
查看>>
[leetcode BY python]1两数之和
查看>>
微信小程序开发全线记录
查看>>
Centos import torchvision 出现 No module named ‘_lzma‘
查看>>
网页设计里的浮动 属性
查看>>
Maximum Subsequence Sum
查看>>
PTA:一元多项式的加乘运算
查看>>
CCF 分蛋糕
查看>>
解决python2.7中UnicodeEncodeError
查看>>