开发者

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

开发者 https://www.devze.com 2022-12-24 10:41 出处:网络 作者: chao09_01
目录Java调用shell命令涉及管道、重定向时不生效Java执行shell遇到的各种问题1、判断子进程是否执行结束2、Process.waitFor()导致当前线程阻塞3、shell脚本中有关联脚本,注意路径4、java连续调用多个脚本5、java执行
目录
  • Java调用shell命令涉及管道、重定向时不生效
  • Java执行shell遇到的各种问题
    • 1、判断子进程是否执行结束
    • 2、Process.waitFor()导致当前线程阻塞
    • 3、shell脚本中有关联脚本,注意路径
    • 4、java连续调用多个脚本
    • 5、java执行.sh脚本文件的时候直接写目录就行
  • 总结

    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"};
                    // 必须加上sphph -c
                    p = Runtime.getRuntime().exec(new String[]{"sh","-c",command});
                    if(0==p.waitFor())
              android      {
                            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().exandroidec(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 (InterrusZvogGWSptedException 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开发者_JAVA入门.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类调用了e编程tc目录下的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);

    总结

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

    0

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    关注公众号