关于使用ProcessBuilder调用shell脚本,以及部署到docker中遇到的问题

一. 将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中的数据读出来,就可以继续运行程序了。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用Java的ProcessBuilder类来远程调用shell脚本ProcessBuilder类允许您启动并控制外部进程。以下是一个示例代码,它使用ProcessBuilder类从远程服务器上运行shell脚本。 ``` import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class RemoteShellExecutor { public static void main(String[] args) throws IOException, InterruptedException { String host = "remote-host"; // 远程服务器IP地址或域名 String user = "remote-user"; // 远程服务器用户名 String password = "remote-password"; // 远程服务器密码 String command = "/path/to/remote/script.sh"; // 远程服务器上的shell脚本路径 // 创建ProcessBuilder对象 ProcessBuilder processBuilder = new ProcessBuilder(); // 设置远程SSH连接命令 processBuilder.command("sshpass", "-p", password, "ssh", user + "@" + host, command); // 启动进程 Process process = processBuilder.start(); // 读取进程输出 BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); String line; while ((line = reader.readLine()) != null) { System.out.println(line); } // 等待进程结束 int exitCode = process.waitFor(); System.out.println("Process exited with code " + exitCode); } } ``` 在上面的示例代码,我们使用ProcessBuilder类创建了一个SSH连接命令,并将其设置为要执行的命令。然后,我们启动进程,并使用一个缓冲读取器来读取进程输出。最后,我们等待进程结束,并打印进程退出代码。请注意,您需要在本地计算机上安装sshpass软件包,以便使用密码连接到远程服务器。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值