解决使用ProcessBuilder踩到的坑及注意事项

作者:monkey_win 时间:2023-11-24 01:37:55 

使用ProcessBuilder踩到的坑

最近使用ProcessBuilder执行命令,命令内容正确,但始终报错命令实行失败,是因为不熟悉ProcessBuilder用法踩到了坑,记录一下。

先看一下我模拟出来的错误

解决使用ProcessBuilder踩到的坑及注意事项

要执行的命令:cp -rf /tmp/monkey/a.log /home/monkey/ 简单的cp命令拷贝一个文件,却报错说文件不存在。确认过文件确实存在该目录下。

解决使用ProcessBuilder踩到的坑及注意事项

查看jdk 中,我使用的ProcessBuilder(***) 源码实现如下,并不是一个单独的字符串String形式,而是支持多个字符串,同时还有List集合方式。

解决使用ProcessBuilder踩到的坑及注意事项

解决使用ProcessBuilder踩到的坑及注意事项

于是想到会不会是ProcessBuilderbuilder不支持包含空格的命令。

动手写了下面的代码进行测试


public class ProcessBuilderDemo {
   /**
    * 测试processBuilder执行cp命令
    * cp /tmp/monkey/a.log /home/monkey/
    * 源路径    args[1]: /tmp/monkey/a.log
    * 目标路径  args[2]: /home/monkey/
    * 方法名    args[3]
    * @param args
    */
   public static void main(String[] args) {
       String src = args[0];
       String tag = args[1];
       String method = args.length == 3 ? args[2] : null;
       if (method != null && method.equals("string")) {
           cmdIsString(src, tag);
       } else {
           cmdIsListOrArray(src, tag);
       }
   }
   /**
    * 执行命令,命令用拼接成一个字符串形式(会包含空格)
    * @param src 源路径
    * @param tag 目标路径
    */
   private static void cmdIsString(String src, String tag) {
       String cmd = "cp";
       cmd = cmd + " -rf" + " " + src + " " + tag;
       System.out.println("command is: " + cmd);
       ProcessBuilder builder = new ProcessBuilder(cmd);
       try {
           Process process = builder.start();
           process.waitFor();
       } catch (Exception e) {
           e.printStackTrace();
           System.out.println(e.toString());
       }
   }
   /**
    * 执行命令,命令各个部分拼接成一个数组或者ArrayList集合
    * 该方法采用数组实现
    * @param src 源路径
    * @param tag 目标路径
    */
   private static void cmdIsListOrArray(String src, String tag) {
       String cmd = "cp";
       // 命令的各个部分组成一个字符串数组,用该数组创建ProcessBuilder对象
       String[] cmds = new String[] {cmd, "-rf", src, tag};
       ProcessBuilder builder = new ProcessBuilder(cmds);
       try {
           Process process = builder.start();
           process.waitFor();
       } catch (Exception e) {
           e.printStackTrace();
           System.out.println(e.toString());
       }
   }
}

果然如我所猜想的一样:包含有空格的命令执行会报错。

以下是cmdIsListOrArray方法,将命令的内容组成字符串的形式执行的结果,而文章第一张图则是直接当做一条完整命令的执行结果。

解决使用ProcessBuilder踩到的坑及注意事项

至于为什么不能好有空格暂时未做深入了解,有带佬可以释疑吗?难道一条完整的命令当做一个字符串它不香嘛?


while(true) {
   伸手党;
}

使用ProcessBuilder执行本地程序的注意事项

错误代码


   public static void main(String[] args) throws IOException, InterruptedException {
       ProcessBuilder processBuilder = new ProcessBuilder("java","-version");
       Process process = processBuilder.start();
       InputStream inputStream = process.getInputStream();
       xxx(inputStream);
   }

private static void xxx(InputStream inputStream) throws IOException {
       BufferedReader input = new BufferedReader(new InputStreamReader(inputStream));
       String ss=null;
       while ((ss=input.readLine())!=null){
           System.out.println(ss);
       }
   }

使用ProcessBuilder类带参执行命令容易出现的两个坑

1、执行后没有任何反映

原因为通过ProcessBuilder运行的参数还没有执行完毕程序就退出了。

通过if(process.isAlive()){process.waitFor();}可以规避此问题,但是需要注意waitFor时程序时阻塞的,如果是持续运行的web项目可以通过开启子线程来执行ProcessBuilder

2、执行后没有任何输出

最恶心的地方,除了getInputStream外还有一个getErrorStream也可以获取数据,而且一般执行的程序数据都会输出在getErrorStream中,所以getInputStream无法获取到数据

处理后的代码


public static void main(String[] args) throws IOException, InterruptedException {
       ProcessBuilder processBuilder = new ProcessBuilder("java","-version");
       processBuilder.redirectErrorStream(true);//将错误流中的数据合并到输入流
       Process process = processBuilder.start();
       if(process.isAlive()){
           process.waitFor();
       }
//        InputStream errorStream = process.getErrorStream();
       InputStream inputStream = process.getInputStream();
//        xxx(errorStream);
       xxx(inputStream);
   }

private static void xxx(InputStream inputStream) throws IOException {
       BufferedReader input = new BufferedReader(new InputStreamReader(inputStream));
       String ss=null;
       while ((ss=input.readLine())!=null){
           System.out.println(ss);
       }
   }

后续发现新的问题,当某个软件会持续向流中写数据,这时流中数据没有被读取完毕(流中存在数据【测试发现流中存在数据并不是一定会阻塞】),会导致waitFor一直陷入阻塞

上述问题处理后的代码(正确使用ProcessBuilder的代码)


public static void main(String[] args) throws IOException, InterruptedException {
       ProcessBuilder processBuilder = new ProcessBuilder("java","-version");
       processBuilder.redirectErrorStream(true);
       Process process = processBuilder.start();
//        通过标准输入流来拿到正常和错误的信息
       InputStream inputStream = process.getInputStream();
       BufferedReader input = new BufferedReader(new InputStreamReader(inputStream));
       String ss=null;
       while ((ss=input.readLine())!=null){
           System.out.println(ss);
       }
       process.waitFor();
   }

复现错误:

1、某个软件持续向流中写数据时,如果流中数据未被读取完毕waitFor一直陷入等待


public static void main(String[] args) throws IOException, InterruptedException {
       ProcessBuilder processBuilder = new ProcessBuilder();
       List<String> meta = new ArrayList<String>();
       meta.add("ffmpeg");
       meta.add("-i");
       meta.add("C:/Users/Lenovo/Desktop/20200801134820261.mp3");
       meta.add("-af");
       meta.add("silencedetect=n=-1dB:d=0.5");
       meta.add("-f");
       meta.add("null");
       meta.add("-");
       processBuilder.command(meta);
       processBuilder.redirectErrorStream(true);
       Process process = processBuilder.start();
       InputStream inputStream = process.getInputStream();
//        BufferedReader input = new BufferedReader(new InputStreamReader(inputStream));
//        String ss=null;
//        while ((ss=input.readLine())!=null){
//            System.out.println(ss);
//        }
       process.waitFor();
       System.out.println("一直阻塞无法执行到这一步");
   }

2、通过下面代码证明上面的观点:当将流中数据读取完毕后waitFor不会阻塞,可执行下一步


public static void main(String[] args) throws IOException, InterruptedException {
   ProcessBuilder processBuilder = new ProcessBuilder();
   List<String> meta = new ArrayList<String>();
   meta.add("ffmpeg");
   meta.add("-i");
   meta.add("C:/Users/Lenovo/Desktop/20200801134820261.mp3");
   meta.add("-af");
   meta.add("silencedetect=n=-1dB:d=0.5");
   meta.add("-f");
   meta.add("null");
   meta.add("-");
   processBuilder.command(meta);
   processBuilder.redirectErrorStream(true);
   Process process = processBuilder.start();
   InputStream inputStream = process.getInputStream();
   BufferedReader input = new BufferedReader(new InputStreamReader(inputStream));
   String ss=null;
   while ((ss=input.readLine())!=null){
       System.out.println(ss);
   }
   process.waitFor();
   System.out.println("正常输出");
}

3、与上面观点产生矛盾的代码:下面代码中的流中仍然存在数据,但是waitFor并没有陷入阻塞,推测原因可能是由于ipconfig与ffmpeg不同,不存在 持续向流中写数据情况,因此waitFor可以正常结束阻塞


public static void main(String[] args) throws IOException, InterruptedException {
       ProcessBuilder processBuilder = new ProcessBuilder();
       List<String> meta = new ArrayList<String>();
       meta.add("ipconfig");
       processBuilder.command(meta);
       processBuilder.redirectErrorStream(true);
       Process process = processBuilder.start();
       InputStream inputStream = process.getInputStream();
//        BufferedReader input = new BufferedReader(new InputStreamReader(inputStream));
//        String ss=null;
//        while ((ss=input.readLine())!=null){
//            System.out.println(ss);
//        }
       process.waitFor();
       System.out.println("正常输出");
   }

来源:https://blog.csdn.net/monkey_win/article/details/103303174

标签:ProcessBuilder,踩坑
0
投稿

猜你喜欢

  • java IO流文件的读写具体实例

    2023-08-21 04:44:34
  • 解析Android 8.1平台SystemUI 导航栏加载流程

    2023-06-23 15:21:21
  • ElasticSearch添加索引代码实例解析

    2023-11-21 03:41:04
  • springcloud配置ssh的问题及解决方法

    2023-11-03 15:48:11
  • Java8新特性之空指针异常的克星Optional类的实现

    2023-08-04 18:59:53
  • C++ Boost MPI接口详细讲解

    2023-11-02 13:35:36
  • C# Invoke,begininvoke的用法详解

    2023-07-21 01:55:55
  • Spring Security OAuth2 实现登录互踢的示例代码

    2023-09-04 19:09:28
  • IntellJ IDEA神器使用技巧(小结)

    2023-08-08 22:01:44
  • Flutter网络请求的3种简单实现方法

    2023-06-21 10:53:22
  • Java8新特性之类型注解_动力节点Java学院整理

    2023-10-10 16:13:07
  • Android学习笔记-保存数据到SQL数据库中(Saving Data in SQL Databases)

    2023-07-28 08:32:20
  • 详解java.lang.reflect.Modifier.isInterface()方法

    2023-07-27 18:25:25
  • Java实现颜色渐变效果

    2023-08-25 11:10:45
  • 使用SpringBoot+EasyExcel+Vue实现excel表格的导入和导出详解

    2023-07-18 18:15:14
  • 深入分析JAVA流程控制语句

    2023-11-20 10:48:32
  • C++语言io流处理基本操作教程示例

    2023-11-02 22:07:39
  • java读取文件字符集示例方法

    2023-11-09 12:35:39
  • 详解SpringBoot注解读取配置文件的方式

    2023-08-05 02:51:16
  • 深入理解Java并发编程之ThreadLocal

    2023-11-21 02:43:42
  • asp之家 软件编程 m.aspxhome.com