Java代码中调用shell脚本和python脚本并获得输出结果(分为小数据量和大数据量)

Java代码中调用shell和python脚本有多种实现方式,通用方式是使用java.lang中的Runtime类新开进程,调用python脚本的一个例子如下(shell同理):

public String python(String pythonPath, String[] params) {
        File file = new File(pythonPath);
        if (!file.exists()){
            return "python脚本不存在!";
        }

        String[] command = Arrays.copyOf(new String[]{"python", pythonPath}, params.length + 2);
        System.arraycopy(params, 0, command, 2, params.length);

        List res = new ArrayList<>();
        try {
            Process process = Runtime.getRuntime().exec(command, null, null);
            process.waitFor();

            Scanner scanner = new Scanner(process.getInputStream());
            while (scanner.hasNextLine()) {
                String line = scanner.nextLine();
                res.add(line);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return "success";
    }

例子中,参数pythonPath就是python脚本的绝对路径,参数params是脚本的参数数组,command就是执行python命令字符串数组,格式就是python + 脚本 + 参数,构造好command后传入exec()中执行新进程,然后调用waitFor()函数等待进程结束,结束后从进程的输入流中获得脚本的输出结果存储到字符串数组中。

乍一看,上面的代码并没有问题,对于少量的输出结果执行后相当完美,但是当脚本的输出结果大小大于inputStream缓冲区大小时,程序会阻塞在waitFor()函数这里,问题就在于脚本的输出结果是在进程执行完之后才读取,一个好的解决办法就是新开一个清理线程来不断清空缓冲区,也就是输出和读取同时进行,代码如下:

    public String python(String pythonPath, String[] params) {
        File file = new File(pythonPath);
        if (!file.exists()){
            return "python脚本不存在!";
        }

        String[] command = Arrays.copyOf(new String[]{"python", pythonPath}, params.length + 2);
        System.arraycopy(params, 0, command, 2, params.length);

        List res = new ArrayList<>();
        try {
            Process process = Runtime.getRuntime().exec(command, null, null);
            
            ClearThread ct = new ClearThread(process);
            ct.start();
            
            process.waitFor();
            Thread.sleep(1000);
            
            ct.setEnd(true);
            res = ct.getRes();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        return "success";
    }

    class ClearThread extends Thread {
        Process process;
        boolean end;
        List res;

        public ClearThread(Process process) {
            this.process = process;
            end = false;
            res = new ArrayList<>();
        }

        @Override
        public void run() {
            if (process == null) {
                return;
            }
            
            Scanner scanner = new Scanner(process.getInputStream());
            while (process != null && !end) {
                while (scanner.hasNextLine()) {
                    String line = scanner.nextLine();
                    res.add(line);
                }
            }
        }

        public void setEnd(boolean end) {
            this.end = end;
        }

        public List getRes() {
            return res;
        }
    }
其中,在脚本执行执行完后调用sleep()让主线程睡眠一秒,否则会导致清理线程可能会还没拿到缓冲区数据就被end标识符结束了(可以用小数据试验一下)。


你可能感兴趣的:(【Java】学习记录)