java调用shell脚本及注意事项说明

目录
  • 需求
  • 问题
  • 介绍
    • 1、通过ProcessBuilder进行调度
    • 2、直接通过系统的Runtime类执行shell
  • 遇到的问题
    • 1、没权限运行
    • 2、调用shell脚本提示:No such file or directory
    • 3、shell脚本输出太大,程序卡死问题
  • 下面提供工具类和自己的shell脚本
    • 工具类
    • 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"

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • java调用远程服务器的shell脚本以及停止的方法实现

    最近接了个需求,要求远程调shell脚本,你没听错!!!需求就一句话,咱是谁,咱是优秀的开发选手.考虑再三,有两种实现方式: 方案一:脚本所在服务器安装一个客户端,也就是自己写的一个小程序,本地通过端口调目标服务器的程序,然后程序调本机上的shell脚本! 优点:通过端口调用,用户不用暴露服务器的账号密码,安全性高 缺点:我们需要一直维护这个客户端程序,而且每接入一台服务器,都得安装该客户端,另外非常考验客户端程序的健壮性. 方案二:本地直接通过IP,服务器账号密码调远程服务器的shell脚本

  • Java远程调用Shell脚本并获取输出信息【推荐】

    1.添加依赖 <dependency> <groupId>ch.ethz.ganymed</groupId> <artifactId>ganymed-ssh2</artifactId> <version>262</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId&g

  • 利用Python+Java调用Shell脚本时的死锁陷阱详解

    前言 最近有一项需求,要定时判断任务执行条件是否满足并触发 Spark 任务,平时编写 Spark 任务时都是封装为一个 Jar 包,然后采用 Shell 脚本形式传入所需参数执行,考虑到本次判断条件逻辑复杂,只用 Shell 脚本完成不利于开发测试,所以调研使用了 Python 和 Java 分别调用 Spark 脚本的方法. 使用版本为 Python 3.6.4 及 JDK 8 Python 主要使用 subprocess 库.Python 的 API 变动比较频繁,在 3.5 之后新增了

  • Java程序去调用并执行shell脚本及问题总结(推荐)

    摘要: 该文章来自阿里巴巴技术协会(ATA)精选集 背景 我们在开发过程中,大部分是java开发, 而在文本处理过程中,主要就是脚本进行开发. java开发的特点就是我们可以很早地进行TDDL, METAQ 等等地对接: 而脚本开发的特点就是在进行批处理的时候非常方便. 背景 我们在开发过程中,大部分是java开发, 而在文本处理过程中,主要就是脚本进行开发. java开发的特点就是我们可以很早地进行TDDL, METAQ 等等地对接: 而脚本开发的特点就是在进行批处理的时候非常方便. 前阵子我

  • Java调用shell脚本解决传参和权限问题的方法

    1. java 执行shell java 通过 Runtime.getRuntime().exec() 方法执行 shell 的命令或 脚本,exec()方法的参数可以是脚本的路径也可以是直接的 shell命令 代码如下(此代码是存在问题的.完整代码请看2): /** * 执行shell * @param execCmd 使用命令 或 脚本标志位 * @param para 传入参数 */ private static void execShell(boolean execCmd, String

  • java调用shell脚本及注意事项说明

    目录 需求 问题 介绍 1.通过ProcessBuilder进行调度 2.直接通过系统的Runtime类执行shell 遇到的问题 1.没权限运行 2.调用shell脚本提示:No such file or directory 3.shell脚本输出太大,程序卡死问题 下面提供工具类和自己的shell脚本 工具类 shell脚本 需求 get方法下载远程zip包,然后zip包解压,取出第一级目录再次进行压缩获取新的压缩zip包. 问题 如果选择使用java代码的IO流操作,在不确定zip包大小的

  • JAVA如何调用Shell脚本

    在实际项目中,Java有时候需要调用C写出来的东西,除了JNI以外,我认为一种比较好的方法是JAVA调用Shell.先把C写出来的make成可执行文件,然后再写一个shell脚本执行该可执行文件,最后是JAVA调用该shell脚本. JAVA调用很简单,例子如下: 首先是shell脚本 #!/bin/sh echo Begin word cluster /home/felven/word2vec/word2vec -train /home/felven/word2vec/resultbig.tx

  • Java调用Shell命令和脚本的实现

    1.介绍 有时候我们在Linux中运行Java程序时,需要调用一些Shell命令和脚本.而Runtime.getRuntime().exec()方法给我们提供了这个功能,而且Runtime.getRuntime()给我们提供了以下几种exec()方法: Process exec(String command) 在单独的进程中执行指定的字符串命令. Process exec(String[] cmdarray) 在单独的进程中执行指定命令和变量. Process exec(String[] cmd

  • Java调用Shell命令的方法

    本文实例讲述了Java调用Shell命令的方法.分享给大家供大家参考.具体如下: 近日项目中有这样一个需求:系统中的外币资金调度完成以后,要将调度信息生成一个Txt文件,然后将这个Txt文件发送到另外一个系统(Kondor)中.生成文件自然使用OutputStreamWirter了,发送文件有两种方式,一种是用写个一个类似于FTP功能的程序,另外一种就是使用Java来调用Shell,在Shell中完成文件的发送操作.我们选择后一种,即当完成外币资金的调度工作后,用Java的OutputStrea

  • java调用shell命令并获取执行结果的示例

    使用到Process和Runtime两个类,返回值通过Process类的getInputStream()方法获取 package ark; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; public class ReadCmdLine { public st

  • Spring Boot调用 Shell 脚本实现看门狗功能

    需要实现看门狗功能,定时检测另外一个程序是否在运行,使用 crontab 仅可以实现检测程序是否正在运行,无法做到扩展,如:手动重启.程序升级(如果只需要实现自动升级功能可以使用 inotify)等功能:最后决定使用 Spring Boot 调用 Shell 脚本来实现 一.脚本 1.1 启动脚本 #!/bin/bash ps -ef | grep "demo-app-0.0.1-SNAPSHOT.jar" | grep -v "grep" if [ "$

  • Java调用Python脚本传递数据并返回计算结果

    需求:最近在使用基于Java编写的Cloudsim 4.0云仿真平台进行虚拟机动态迁移实验,由于中间有需要用到深度强化学习算法,因此需要将集群的状态表示为二维数组,比如物理机的计算能力Mips,RAM,带宽等等.希望将这样的二维数组传入到带torch等第三方库的Python脚本进行深度强化学习训练,所以就有二维int数组的传入,和从Python计算后的结果返回读取这个需求 一.实现思路:将Java中的data结构化为字符串,以命令行参数的形式传入Python中 目前有几种Java调用Python

随机推荐