我要做的是一个脚本的管理及控制系统,然后脚本之间存在依赖关系,首先需要一个任务调度模块,完成job(脚本集)Task(脚本)的关系控制及正确顺序执行,然后一个控制信息的管理系统,一个驱动脚本执行的模块,大概像是下面的一种结构。
这两篇文章描述的是脚本驱动模块,首先我们需要明确要完成的功能。
启动:可以通过传递shell命令的形式驱动脚本的执行
查看:可以通过传递shell命令的形式查看当前shell脚本的进程状态
终止:强制传递kill该子进程或通过调用process的函数终止外部的shell进程
Java 操作外部程序的方式有这么几种:Runtime、Process(依赖Runtime)、ProcessBuilder
Runtime(使用方式):
1、exec方法接收一个命令然后执行,通过该方法可以执行和cmd中用法一样命令。比如,Runtime.getRuntime().exec(“ls”),就和cmd中执行ls效果一样了。
2、freeMemory()可以查看当前虚拟机内存中空闲内存还有多少。
3、totalMemory()可以查看当前虚拟机使用的总内存大小。
4、maxMemory()可以查看JVM的最终可以使用的最大内存是多少。
5、availableProcessors()可以查看本机有多少处理器,即本机处理器是多少核。
6、exit(int)方法可以退出当前Java程序的运行,System.exit(int)方法就是调用了Runtime.exit(int)方法来退出运行的。
process(依赖于Runtime):
1、waitFor()是让当前主进程等待这个process指向的子进程执行完成。
2、exitValue()可以查看process指向的子进程执行完的退出值,0代表是正常运行结束。
3、destroy()和destroyForcibly()可以终止process子进程的运行,后者是强制终止,前者与平台终止进程的具体实现有关。
ProcessBuilder
1、创建ProcessBuilder不需要通过Runtime,而Runtime.getRimtime().exec(string)正是调用了ProcessBuilder的构造方法来创建子进程并执行的。
传参问题:当传递的参数字符串中包含空格时,会把参数截断,默认为参数只到空格处。
解决:将shell 命令或脚本 和参数 放在一个 数组中,然后将数组传入exec()方法中。
2、ProcessBuilder的构造方法接收一个命令参数的数组形式,其中,第一个元素代表要执行的系统命令,后面的元素代表要传给该命令的参数。
3、调用.start()方法运行之后,就可以获得该子进程的Process引用了,然后就可以调用Process的方法进行处理。
ProcessBuilder核心总结:
核心使用:
start():启动进程
waitFor();等待命令结束后返回
其他:
command() 返回此进程生成器的操作系统程序和参数。
command(List<String> command)设置此进程生成器的操作系统程序和参数。
command(String... command) 设置此进程生成器的操作系统程序和参数。
directory()返回此进程生成器的工作目录。
directory(File directory) 设置此进程生成器的工作目录。
environment() 返回此进程生成器环境的字符串映射视图。 可以获取到系统默认的环境变量
redirectErrorStream()通知进程生成器是否合并标准错误和标准输出。
redirectErrorStream(boolean redirectErrorStream)设置此进程生成器的 redirectErrorStream 属性。
Process:
getInputStream() :标准输出
getErrorStream() :标准错误输出
info:进程当前信息
pid:当前进程号
权限问题:存在脚本无权限执行的问题
解决:ProcessBuilder builder = new ProcessBuilder("/bin/chmod", "755",scriptPath);
注意:
process创建的子进程没有自己的控制台或终端,其所有的io操作都是通过(输入流、输出流、错误流)重定向到父进程中
更加合适的选择:
Java ProcessBuilder,但需要注意Error信息的收集
以上几种方式已经完成了对应的Java调用本地的外部程序(此处是shell),但很多时候我们需要完成对于非本地机器的shell调用,这时候我们需要使用Java的远程连接工具,比如说Ganymed SSH-2 for Java。
Ganymed SSH-2 for Java是用纯Java实现SSH-2协议的一个包。可以利用它直接在Java程序中连接SSH服务器。Ganymed SSH-2支持SSH对话(远程命令执行和shell访问),本地和远程端口转发,本地数据流转发,X11转发和SCP。这些都没有依赖任何JCE provider,而且所有这些都包含加密的功能。
看到这里我们已经能够驱动外部程序执行了,剩下的是外部程序执行(进程)的管理策略。
假如使用ProcessBuilder进行构建基础模块
首先要完成对于进程的驱动执行,我们可以获得对应进程的相关信息(pid、执行时间、系统环境等)。
是时候看一眼代码了
List<String> cmd=Arrays.asList("/bin/bash","-c","./test.sh");
String workDir="/Users/zouzhiquan/workspace/vim";
ProcessBuilder processBuilder=new ProcessBuilder(cmd);
processBuilder.directory(new File(workDir));
processBuilder.environment();
//到这一步,已经完成了相关的准备工作
processBuilder.redirectError();
然后我们需要重定向Error,方便补货
Process process=processBuilder.start();
//启动对应的外部程序,并获取对应的process,这里可以简单理解为获取你正在执行的进程对象
process.info();
process.pid();
process.getInputStream();
process.getErrorStream();
//可以使用以上方法完成对于进程信息的采集。
完成启动之后,剩下的便是进程的终止、进程的查看、重试逻辑(Error的处理策略)
这时候仅需要根据已有的信息,对于进程进行操作即可,拿到pid一切就都好说了,但是对应策略逻辑要注意上篇发的操作系统基础,注意进程的操作逻辑。
如果是远程机器使用Ganymed SSH-2 for Java 完成对应的远程连接、命令传递即可。