Java调用shell命令涉及管道、重定向时不生效问题及解决

 更新时间:2022年12月23日 16:01:48   作者:chao09_01  
这篇文章主要介绍了Java调用shell命令涉及管道、重定向时不生效问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

Java调用shell命令涉及管道、重定向时不生效

近日,因项目需求需要用java调用shell命令实现清理过时图片任务,发现代码生成出来的shell命令在linux系统后台直接执行,可以实现效果,但是,经过java代码运行,则达不到预期效果。

经研究发现,因为该shell命令涉及了管道,这情况就有点不一样了,下面是针对Java调用shell命令涉及管道、重定向时不生效问题的解决方法

参考代码如下:

public class Test
{
        /**
         * @param args
         * @throws IOException
         * @throws InterruptedException
         */
        public static void main(String[] args) throws IOException, InterruptedException
        {
                Process p;
                String command = "find /opt/Img/ \"2019-11-22-*.jpg\" | xargs rm -rf"};
                // 必须加上sh -c
                p = Runtime.getRuntime().exec(new String[]{"sh","-c",command});
                if(0==p.waitFor())
                {
                        System.out.println("Command execute result is OK!");
                }
                else
                {
                        System.out.println("Command execute result is fail......");
                }
        }
}

Java执行shell遇到的各种问题

1、判断子进程是否执行结束

有的时候我们用java调用shell之后,之后的操作要在Process子进程正常执行结束的情况下才可以继续,所以我们需要判断Process进程什么时候终止。

Process类提供了waitFor()方法。该方法导致当前线程等待,直到Process线程终止。

Process.waitFor()是有一个int类型返回值的,当返回值为0的时候表Process进程正常终止。否则一般是脚本执行出错了(我遇到的一般是这种情况)。

2、Process.waitFor()导致当前线程阻塞

有的时候我们发现调用waitFor()方法后,java主线程会一直阻塞在waitFor()处,阻塞的原因是什么呢?分析一下:

Java在执行Runtime.getRuntime().exec(jyName)之后,Linux会创建一个进程,该进程与JVM进程建立三个管道连接,标准输入流、标准输出流、标准错误流,假设linux进程不断

向标准输出流和标准错误流写数据,而JVM却不读取,数据会暂存在linux缓存区,当缓存区存满之后导致该进程无法继续写数据,会僵死,导致java进程会卡死在waitFor()处,

永远无法结束。

解决办法:java进程在waitFor()前不断读取标准输出流和标准错误流:

        //jyName  解压脚本路径
        String fileName=fileList.get(0).toString().substring(fileList.get(0).toString().lastIndexOf(File.separator)+1);
        String  jyName="/etc/zxvf.sh "+fileName;
        try {
            Process p0 = Runtime.getRuntime().exec(jyName);
            //读取标准输出流
            BufferedReader bufferedReader =new BufferedReader(new InputStreamReader(p0.getInputStream()));
            String line;
            while ((line=bufferedReader.readLine()) != null) {
                System.out.println(line);
            }    
            //读取标准错误流
            BufferedReader brError = new BufferedReader(new InputStreamReader(p0.getErrorStream(), "gb2312"));
            String errline = null;
            while ((errline = brError.readLine()) != null) {
                 System.out.println(errline);
            }
            //waitFor()判断Process进程是否终止,通过返回值判断是否正常终止。0代表正常终止
            int c=p0.waitFor();
            if(c!=0){
                baseRes.put("desc", "软件升级失败:执行zxvf.sh异常终止");
                baseRes.setReturnFlag(false);
                return baseRes;
            }
        } catch (IOException e1) {
            baseRes.put("desc", "软件升级失败:文件解压失败");
            baseRes.setReturnFlag(false);
            return baseRes;
        } catch (InterruptedException e1) {
            baseRes.put("desc", "软件升级失败:文件解压失败");
            baseRes.setReturnFlag(false);
            return baseRes;
        }

也可以在执行Runtime.getRuntime().exec(jyName)之后另外再启动两个线程分别读取标准错误流和标准输出流

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
 
public class ExcuteThread extends Thread {
    private String name;
 
    public ExcuteThread(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        try {
            Process p = Runtime.getRuntime().exec(name);
            InputStream fis = p.getInputStream();
            final BufferedReader brError = new BufferedReader(
                    new InputStreamReader(p.getErrorStream(), "gb2312"));
            InputStreamReader isr = new InputStreamReader(fis, "gb2312");
            final BufferedReader br = new BufferedReader(isr);
            Thread t1 = new Thread() {
                public void run() {
                    String line = null;
                    try {
                        while ((line = brError.readLine()) != null) {
                            // System.out.println(line);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        try {
                            if (brError != null)
                                brError.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            };
            Thread t2 = new Thread() {
                public void run() {
                    String line = null;
                    try {
                        while ((line = br.readLine()) != null) {
                            // System.out.println(line);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        try {
                            if (br != null)
                                br.close();
                        } catch (IOException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }
            };
            t1.start();
            t2.start();
 
        } catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        } finally {
        }
 
    }
 
}

3、shell脚本中有关联脚本,注意路径

就是shell脚本中还要执行其他脚本,这时候就是注意一个路径的问题,这个问题也是我找了好长时间的一个问题。

Process p=Runtime.getRuntime().exec(“/etc/a.sh”)

在Test.java类调用了etc目录下的a.sh脚本, a.sh脚本中执行etc目录下的b.sh脚本,原来我在a.sh脚本中写的是./b.sh。其实这样linux是找不到b.sh的,因为我们执行是在

Test.class目录下调用的/etc/a.sh  所以当a.sh中执行./b.sh的时候他会在Test.class目录下寻找,所以找不到,所以a.sh中要写成/etc/b.sh

4、java连续调用多个脚本

String[] cmd = { "/bin/sh", "-c", "rm -rf /installation/upgrade/ ; mkdir /installation/upgrade/" };
Process p = Runtime.getRuntime().exec(cmd);
p.waitFor();

就是这种数组的方式。

5、java执行.sh脚本文件的时候直接写目录就行

例如这样:Runtime.getRuntime().exec(“/etc/a.sh”)

java 直接执行语句的时候需要加上"/bin/sh"  例如这样:

String name="/bin/sh cd /installation/upgrade/ip89_install_packet";
Process p = Runtime.getRuntime().exec(name);

总结

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

相关文章

  • 在IntelliJ IDEA中为自己设计的类库生成JavaDoc的方法示例

    在IntelliJ IDEA中为自己设计的类库生成JavaDoc的方法示例

    这篇文章主要介绍了在IntelliJ IDEA中为自己设计的类库生成JavaDoc的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • SpringSecurity如何实现配置单个HttpSecurity

    SpringSecurity如何实现配置单个HttpSecurity

    这篇文章主要介绍了SpringSecurity如何实现配置单个HttpSecurity,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08
  • Java基础类之ArrayUtils工具类详解

    Java基础类之ArrayUtils工具类详解

    这篇文章主要介绍了java.ArrayDeque类使用方法,java.ArrayDeque类提供了可调整大小的阵列,并实现了Deque接口,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • spring集成redis cluster详解

    spring集成redis cluster详解

    这篇文章主要介绍了spring集成redis cluster详解,分享了maven依赖,Spring配置,增加connect-redis.properties 配置文件等相关内容,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • MyBatis中的ResultMap的association和collection标签详解

    MyBatis中的ResultMap的association和collection标签详解

    这篇文章主要介绍了MyBatis中的ResultMap的association和collection标签详解,主要包括association标签常用参数及id & result标签参数详解,本文给大家介绍的非常详细,需要的朋友可以参考下
    2022-10-10
  • 使用迭代器Iterator遍历Collection问题

    使用迭代器Iterator遍历Collection问题

    这篇文章主要介绍了使用迭代器Iterator遍历Collection问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • Java并发编程之ReentrantLock解析

    Java并发编程之ReentrantLock解析

    这篇文章主要介绍了Java并发编程之ReentrantLock解析,ReentrantLock内容定义了一个抽象类Sync,继承自AQS,而不是自己去继承AQS,所有对ReentrantLock的操作都会转化为对Sync的操作,需要的朋友可以参考下
    2023-12-12
  • Java基于WebMagic爬取某豆瓣电影评论的实现

    Java基于WebMagic爬取某豆瓣电影评论的实现

    这篇文章主要介绍了Java基于WebMagic爬取某豆瓣电影评论的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • Java内存模型相关知识总结

    Java内存模型相关知识总结

    这篇文章主要介绍了Java内存模型相关知识总结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-10-10
  • Java基于socket实现简易聊天室实例

    Java基于socket实现简易聊天室实例

    这篇文章主要介绍了Java基于socket实现简易聊天室的方法,实例分析了java基于socket实现聊天室服务端与客户端的相关技巧,需要的朋友可以参考下
    2015-05-05

最新评论