文章描述了在Java应用中调用被打包进jar的shell脚本以及在Docker环境中执行这些脚本时遇到的问题,包括脚本权限、bash环境、date命令的兼容性以及Java进程管理。解决方案包括通过绝对路径引用脚本、在Dockerfile中添加权限设置、安装bash并修改脚本头、调整date命令格式以及处理Java进程输出的阻塞问题。 摘要由CSDN通过智能技术生成
一. 将shell脚本放在resouces目录下,打成jar包,在linux上使用java -jar运行时,无法运行指定shell脚本。

这是因为shell脚本被打进jar之后,不再以完整的.sh脚本存在,而是经过了打包程序的编译。
解决方法时:把shell脚本和jar包拷贝到同一个路径下,在代码中通过绝对路径引用shell脚本,进行执行。此过程注意为shell脚本赋权。

 String path = System.getProperty("user.dir");
 ProcessBuilder pb = new ProcessBuilder(command);
        pb.directory(new File(path));
        int runningStatus = 0;
        try {
            Process p = pb.start();
            runningStatus = p.waitFor();
        } catch (InterruptedException e) {
            log.error("error:" + e.getLocalizedMessage());
        } catch (IOException e) {
            throw new RuntimeException(e);
二. 在Docker镜像中执行时,提示 error=2, No such file or directory。

在Docker中执行shell脚本时,要将shell脚本拷贝进镜像中。但是拷贝进去之后,发现报No such file or directory。仔细检查了一下,怀疑是因为没有为shell脚本增加权限。然后在Dockerfile中增加了如下内容:

RUN chmod u+x /opt/gansu_crontab_download_gfd_10D.sh
RUN chmod u+x /opt/delete_gansu_tempfile.sh
RUN chmod u+x /opt/zj_sh_crontab_download_gfd_10D.sh
RUN chmod u+x /opt/delete_zjsh_tempfile.sh

但是之后运行提示没有找到 ~/.bashsc 和 ~/.bash_profile。下面是我的shell脚本最初部分内容:

#!/bin/sh
set -x
source ~/.bashrc
source ~/.bash_profile

通过仔细查找发现是因为我引用的是openjdk:8-jdk-alpine的镜像,此镜像中是没有提供这些环境的。然后考虑安装bash,在Dockerfile中增加了如下内容:

# 安装bash
RUN apk add --no-cache bash

因为Docker中默认使用的是 /bin/sh,我又将代码中的命令改成了以 /bin/bash 调用:

ProcessBuilder pb = new ProcessBuilder("/bin/bash /opt/gansu_xxx.sh");

但是在这一步无论怎么搞都是无法运行,相反的,进入Docker容器内,直接./gansu_xxx.sh就能直接运行。然后反复检查。最后发现我之前的shell脚本头是

#!/bin/sh

应该改为:

#!/bin/bash

只有这样才能正确被bash调用。到此程序终于开始运行了。但还是有一些问题。

三. docker中shell的时间函数用不了了?

我的shell脚本有获取前一天日期的脚本,但是无法获取,脚本如下:

DateInfo=$(date -d'-1 day' "+%Y%m%d")

但是在运行的时候,提示我语法不对。然后我仔细检查了一下发现 openjdk:8-jdk-alpine镜像用的shell命令是busybox的,它里面的date命令和传统的linux下的date有点区别,其中的 -d 后面的参数有些不一样。最终改成了下面的语法:

DateInfo=$(date -d@"$(( `date +%s`-86400))" +"%Y%m%d")
四. java调用shell成功,但是执行到一半就莫名终止了。

关于这个问题,可能也比较有特殊性。经过一番检查在我的shell脚本里有下面一段:

#一些业务逻辑 }> ${workDir}/gfs.0p25.${DateInfo}${TimeInfo}.f${stepStr}.log 2>&1 & step=`expr $step + $interval` sleep 2

这段意思是在后台做一些标准输出,但是java的ProcessBuilder是能获取这些输出的。如果没有read出来,就会堆积在缓存中,而最坑的是,这个缓存有一个固定大小,如果达到最大值,就会阻塞等待缓存被读出,而我的java代码里一直没有对其操作,所以等到输出的内容足够多,就一直阻塞,解决办法是,在java中增加如下代码:

ProcessBuilder pb = new ProcessBuilder(command);
        pb.directory(new File(path));
        int runningStatus = 0;
        Process process;
        try {
            process = pb.start();
            BufferedReader errReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
            BufferedReader staReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            new Thread(() -> {
                String line;
                try {
                    while ((line = staReader.readLine()) != null) {
                } catch (IOException e) {
                    e.printStackTrace();
            }).start();
            new Thread(() -> {
                String line;
                try {
                    while ((line = errReader.readLine()) != null) {
                        if (line.startsWith("step=")) {
                            log.info("----开始下载:{}-----", line);
                } catch (IOException e) {
                    e.printStackTrace();
            }).start();
            runningStatus = process.waitFor();
            errReader.close();
            staReader.close();
        } catch (InterruptedException e) {
            log.error("error:" + e.getLocalizedMessage());
        } catch (IOException e) {
            throw new RuntimeException(e);

增加两个线程(也可以不使用线程)获取getErrorStream()getInputStream(),把这两个inputStream中的数据读出来,就可以继续运行程序了。

使用Jenkins的版本为2.138.3,下载war包后启动即可运行: # 指定使用8080端口,可自定义 java -jar jenkins.war --httpPort=8080 建议后台启动,命令如下: # 1. 启动 # 指定后台启动 nohup java -jar jenkins.war --httpPort=8080 & # 再指定日志路径 appending o...
ProcessBuilder可以用来运行外部应用程序。在我使用过程发现了一些需要注意的地方,这些小问题也着实费了我很长时间去调试。不过既然知道了,就最好写出来,避免后来者再次出错。 首先,尽管ProcessBuilder可以用来运行外部程序,但不是所有能在shell执行的命令都能被ProcessBuilder完美执行。以Windows系统为例,Windows批处理程序使用输出流/输入流/
public class ProcessBuilderDemo { public void exeLocalCommand(String exportCmd) throws Exception{ ProcessBuilder builder; String charSet;
使用processBuilder执行shell命令并与之交互 private void execReloadAll(){ List<String> cmds = new ArrayList<String>(); commands.add("ls"); commands.add("-l"); commands.add("/"); ProcessBuilder pb=new ProcessBuilder(c
我正在尝试从Java执行bash脚本,它返回错误/ bin / bash:’/ home / nika / NetBeansProjects / Parallel Framework / process-executor.sh’:没有这样的文件或目录,我正在使用ubuntu使用netbeans8& 14.04 jdk8. 这是我的代码: public class Process { public static void main(String[] args) { try { Process类是一个抽象类(所有的方法均是抽象的),封装了一个进程(即一个执行程序)。 Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的方法。 ProcessBuilder.start() 和 Runtime.exec 方法创建一个本机进程,并返回 Process 子类的一个实例,该实例可用来控制进程并获取相关信息。创建的子进程没有自己的终端或控制台。它的所有标准 io(即 stdin,stdout,stderr)操作都将通过三...