Java调用Shell问题整理

背景

java可以通过Runtime来调用其他进程,如cmd命令,shell文件或脚本等。

基本用法

Runtime执行时返回一个Process对象,利用该对象完成脚本执行。下面的例子中,Linux的/home/目录下有一个删除指定日期文件的脚本deletefile.sh,Java调用该脚本的方法如下。

 /**
     * 删除指定日期的文件
     * @param date  yyyy-MM-dd格式
     */
    private static  void setSystemDate(String date){
        Process process = null;
        String command1 = "/bin/sh /home/deletefile.sh "+date;
        System.out.println(command1);
        try {
            process = Runtime.getRuntime().exec(command1);
            //必须等待该进程结束,否则时间设置就无法生效
            process.waitFor();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }finally{
            if(process!=null){
                process.destroy();
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

缓冲区满问题

如果脚本执行过程中产生大量的控制台输出信息,这种信息会被Shell进程输出到内存缓冲区中,而上述用法中作为父进程的java进程并没有处理缓冲区内容,那么就会出现缓冲区满,Java进程挂起的情况。
解决办法是,使用Java线程读取Shell脚本的输出信息。

   public static List<String> executeShell(String shpath, String var){
        
        //String shpath="/test/test.sh";   // sh 路径
        //String var="201102";             // sh 参数
        String shellVar = (var==null)?"":var;
        String command1 = "chmod 777 " + shpath; // 为 sh 添加权限
        String command2 = "/bin/sh " + shpath + " " + shellVar; 
        final List<String> strList = new ArrayList<String>(); 
        Process process1 = null;
        BufferedReader in = null;
        try {
            process1 = Runtime.getRuntime().exec(command1); // 执行添加权限的命令
            process1.waitFor(); // 如果执行多个命令,必须加上
            final Process process2 = Runtime.getRuntime().exec(command2);
           
            //处理InputStream的线程  
            new Thread() {  
                @Override  
                public void run() {  
                    BufferedReader in = new BufferedReader(new InputStreamReader(process2.getInputStream()));   
                    String line = null;  
                    try{  
                        while((line = in.readLine()) != null) {  
                        	strList.add(line);
                        }  
                    }catch(IOException e) {                         
                        e.printStackTrace();  
                    }finally {  
                        try {  
                            in.close();  
                        }   
                        catch (IOException e) {  
                            e.printStackTrace();  
                        }  
                    }  
                }  
            }.start();  

            //处理ErrorStream的线程  
            new Thread() {  
                @Override  
                public void run(){  
                    BufferedReader err = new BufferedReader(new InputStreamReader(process2.getErrorStream()));   
                    String line = null;  
                    try{  
                        while((line = err.readLine()) != null)  
                        {  
                        	strList.add(line); 
                        }  
                    }catch(IOException e){                         
                        e.printStackTrace();  
                    }finally{  
                        try{  
                            err.close();  
                        }catch (IOException e){  
                            e.printStackTrace();  
                        }  
                    }  
                }  
            }.start(); 
            
            process2.waitFor();
 
        } catch (IOException e) {

        } catch (InterruptedException e) {
           
        }finally {
            if(in != null){
                try {
                    in.close();
                } catch (IOException e) {
 
                }
            }
            if(process1 != null){
                process1.destroy();
            }
        }
        return strList;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81

线程阻塞问题

如果执行的shell脚本中有等待输入命令,比如more操作,那么在使用java进程读取缓冲区内容的时候,会出现线程阻塞等待的问题。
例如,下面的脚本读取文件内容。

filePath=/home/software.info
softType=$(more $filePath)
echo $softType
  • 1
  • 2
  • 3

上述三行代码很简单地输出了一个文件的内容,而且这个文件很短,只有一行,在Linux下执行没有问题,但是如果用Java调用executeShell方法执行的话,处理InputStream的线程会出现阻塞等待问题,根源在于脚本中的more命令,它会等待控制台的输入,从而导致了Java进程的阻塞。
解决办法就是避免在shell脚本中使用more命令,用cat命令替换即可。

管道命令不执行问题

使用Process执行shell命令时,如果命令中包含管道命令,直接执行会得不到正确结果,解决办法是使用/bin/sh,将/bin/sh放在待执行的shell命令的前面,可保证管道命令的正确执行。

String command1 = "/bin/sh ifconfig | grep if= | awk '{print $1,$6}'";

  • 1
  • 2

启示录

究竟在Java中调用Shell是不是明智的呢?

posted @ 2020-10-21 10:49  牧之丨  阅读(680)  评论(0编辑  收藏  举报