在最近做的项目当中,需要用 Java调用基于python写的两个不同的模型运行 并且得到实验结果:
1.在服务器上运行的文本识别模型
2.在本地Ubuntu中annacoda 虚拟环境中运行的影像识别模型
由于两个模型都是在终端执行,于是考虑用java代码直接调起终端,然后让终端执行指定的shell命令,测试代码如第二节;
由于模型的启动模型在不同的项目路径下,这种情况下指令需要好几条,有点繁琐,于是写了一个脚本,考虑用java直接调用shell脚本;
1.在实际项目当中,如果指令比较简单,可以直接把需要执行的指令传到Runtime.getRuntime().exec()中的参数。百度之后发现exec()有如下几种参数:
cmdarray: 包含所调用命令及其参数的数组。
command: 一条指定的系统命令。
envp: 字符串数组,其中每个元素的环境变量的设置格式为name=value;如果子进程应该继承当前进程的环境,则该参数为 null。
dir: 子进程的工作目录;如果子进程应该继承当前进程的工作目录,则该参数为 null。
Process exec(String command) 在单独的进程中执行指定的字符串命令。 Process exec(String[] cmdarray) 在单独的进程中执行指定命令和变量。 --不指定环境即默认环境 Process exec(String[] cmdarray, String[] envp) 在指定环境的独立进程中执行指定命令和变量。 Process exec(String[] cmdarray, String[] envp, File dir) 在指定环境和工作目录的独立进程中执行指定的命令和变量。 Process exec(String command, String[] envp) 在指定环境的单独进程中执行指定的字符串命令。 Process exec(String command, String[] envp, File dir) 在有指定环境和工作目录的独立进程中执行指定的字符串命令。 复制代码
添加依赖包:
<dependency> <groupId>com.jcraft</groupId> <artifactId>jsch</artifactId> <version>0.1.50</version> </dependency> 复制代码
代码:ls;pwd
public void runPicmodels() { try { String shpath = "/home/hzhao/project_bj"; String[] params = new String[] { "/bin/sh", "-c", "ls;pwd"}; Process ps=Runtime.getRuntime().exec(params); ps.waitFor(); BufferedReader bufrIn = new BufferedReader(new InputStreamReader(ps.getInputStream(), "UTF-8")); BufferedReader bufrError = new BufferedReader(new InputStreamReader(ps.getErrorStream(), "UTF-8")); // 读取输出 result是shell中的输出 StringBuilder result = new StringBuilder(); String line = null; while ((line = bufrIn.readLine()) != null || (line = bufrError.readLine()) != null) { result.append(line).append('/n'); } System.out.println(result); } catch (Exception e) { e.printStackTrace(); } } 复制代码
需要注意的是,在调用时需要执行waitFor()函数,因为shell进程是JAVA进程的子进程,JAVA作为父进程需要等待子进程执行完毕。
1.写shell脚本---测试脚本
#!/usr/bin/sh python /home/hzhao/sys.py source activate zh_py35 cd /home/hzhao/project_bj/detection_pub pwd python /home/hzhao/sys.py echo ------running------- python __main__.pyc echo ------success------- 复制代码
2.调用shell脚本:exec函数,参数编程shell文件,可以添加参数;
import java.io.BufferedReader; import java.io.InputStreamReader; public class RunShell { public static void main(String[] args){ try { String shpath="/home/hzhao/Project/note.sh"; Process ps = Runtime.getRuntime().exec(shpath); ps.waitFor(); BufferedReader br = new BufferedReader(new InputStreamReader(ps.getInputStream())); StringBuffer sb = new StringBuffer(); String line; while ((line = brreadLine()) != null) { sb.append(line).append("/n"); } String result = sbtoString(); System.out.println(result); } catch (Exception e) { e.printStackTrace(); } } } 复制代码
1.ProcessBuilder
2.Runtime
3.读缓冲区:因为shell脚本有时候有echo输出或打印,导致缓冲区被用完了,为了避免这种情况,需要用将缓冲区数据读出来。同时可以打印shell具体运行状态。
BufferedReader br = new BufferedReader(new InputStreamReader(ps.getInputStream())); StringBuffer sb = new StringBuffer(); String line; while ((line = brreadLine()) != null) { sb.append(line).append("/n"); } String result = sbtoString(); System.out.println(result); 复制代码
4.因为其中一个模型只能运行在本地annacoda虚拟环境中,而java调用的时候总是不能通过source activate 激活这个环境。 后面以为是环境变量路径没有配置的问题,尝试在envp中添加环境变量,失败; 查找网络之后,发现原来java在调用shell的时候,默认用的是系统/bin/下的指令。特别是你用root权限运行的时候。 这时候,你要在/bin下加软链了。针对我上面的例子,就要在/bin下加软链。--这个未尝试。后面封装服务解决。