JAVA如何调用系统命令
简介:本章主要简述了java应用程序执行的过程中,对操作系统输入命令执行,并获取操作系统执行结果。如使用调用mvn命令构建工程,其它其它的java应用程序,使用第三方服务功能等。大部分时候我们是不需要知道这个技术的,但总有意外。
实现原理:通过http请求本地地址建立链接,得到请求的输入流和输出流,通过输入流添加操作系统能执行的命令,通过输入流获取操作系统执行打印结果。(自我理解)
主要类:Process
实现方式类:Runtime与jdk1.5之后的ProcessBuilder
关系:Runtime类克隆一个与当前java虚拟机环境相同一个进程返回一个Process类
注意:每个Java应用程序都有一个Runtime类实例,使应用程序能够与其运行的环境相连接。可以通过getRuntime方法获取当前运行时环境。 应用程序不能创建自己的Runtime类实例。
Runtime API
Process exec(String command)
在单独的进程中执行指定的字符串命令。
Process exec(String command, String[] envp)
在指定环境的单独进程中执行指定的字符串命令。
Process exec(String command, String[] envp, File dir)
在有指定环境和工作目录的独立进程中执行指定的字符串命令。
Process exec(String[] cmdarray)
在单独的进程中执行指定命令和变量。
Process exec(String[] cmdarray, String[] envp)
在指定环境的独立进程中执行指定命令和变量。
Process exec(String[] cmdarray, String[] envp, File dir)
在指定环境和工作目录的独立进程中执行指定的命令和变量。
command:一条指定的系统命令。
envp:环境变量字符串数组,其中每个环境变量的设置格式为name=value;如果子进程应该继承当前进程的环境,则该参数为null。
dir:子进程的工作目录;如果子进程应该继承当前进程的工作目录,则该参数为null。
cmdarray:包含所调用命令及其参数的数组。
Runtime例子
public class CommandUtil {
/**
* 执行操作系统命令
* @param path 执行路径如windows的E:/test/,linux的: /usr/util/
* @param commands
* @return
*/
public static List<String> command(String path, List<String> commands) {
List<String> rspList = new ArrayList<String>();
//获取这个进程 Runtime对象
Runtime run = Runtime.getRuntime();
try {
//第一个参数是:首先执行环境命令,不同操作开启命令方式不一样
//第二个参数是:环境变量参数(jvm的环境),这里设置为null则表示和主环境一致
//第三个参数是:设置命令起始执行地址如windows:E:\\test\;linux:/use/util/;
// 相当于执行了一次cd命令
Process proc = run.exec(getOsCmd(), null, new File(path));
//获取正确的输出结果
BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream(),"GB2312"));
//获取错误的输出结果
BufferedReader err = new BufferedReader(new InputStreamReader(proc.getErrorStream(),"GB2312"));
//获取一个写的流,并设置true则每次执行一次则刷新缓冲区
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(proc.getOutputStream())), true);
//变量执行命令(可执行cd)
for (String line : commands) {
out.println(line);
}
out.println("exit");// 这个命令必须执行,否则in流不结束。
String rspLine = "";
while ((rspLine = in.readLine()) != null) {
rspList.add(rspLine);
}
proc.waitFor();//关闭链接
in.close();//关闭输入流
err.close();//关闭输入流
out.close();//关闭输出流
proc.destroy();//结束
} catch (IOException e1) {
e1.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return rspList;
}
}
//获取当前系统环境并设置新窗口
public static String getOsCmd() {
Properties props = System.getProperties(); //获得系统属性集
String osName = props.getProperty("os.name"); //操作系统名称
if (osName.toLowerCase().contains("linux")) {
return "/bin/bash";
} else if (osName.toLowerCase().contains("windows")) {
return "cmd";
} else {
throw new BaseRuntimeException("服务器不是linux|windows操作系统");
}
}
ProcessBuilder API
//构造方法
ProcessBuilder processBuilder = new ProcessBuilder();
//processBuilder.command("ping","127.0.0.1");
//利用指定的操作系统程序和参数构造一个进程生成器。
ProcessBuilder(List<String> command)
//利用指定的操作系统程序和参数构造一个进程生成器。
ProcessBuilder(String… command)
//方法
//返回此进程生成器的操作系统程序和参数。
command()
//设置此进程生成器的操作系统程序和参数。
command(List<String> command)
//设置此进程生成器的操作系统程序和参数。
command(String… command)
//返回此进程生成器的工作目录。
directory()
//设置此进程生成器的工作目录。
directory(File directory)
//返回此进程生成器环境的字符串映射视图。 environment方法获得运行进程的环境变量,得到一个Map,可以修改环境变量
environment()
//返回进程生成器是否合并标准错误和标准输出;true为合并,false为不合并
redirectErrorStream()
//设置此进程生成器的 redirectErrorStream 属性。默认值为false不合并
redirectErrorStream(boolean redirectErrorStream)
//使用此进程生成器的属性启动一个新进程。
start()
ProcessBuilder 例子
public class CommandUtil {
/**
* 执行操作系统命令
* @param path 执行路径如windows的E:/test/,linux的: /usr/util/
* @param commands
* @return
*/
public static List<String> command(String path, List<String> commands) {
List<String> rspList = new ArrayList<String>();
try {
String line = null;
commands.add(0,getOsCmd())
commands.add("exit");
ProcessBuilder pd= new ProcessBuilder(commands);
//设置命令起始地址如windows:E:\\test\;linux:/use/util/
pd.directory(new File(path));
pd.redirectErrorStream(true);//正确与错误日志一起输出
Process p = pd.start();//启动
//读取返回结果
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream(),"GB2312"));
while((line = in.readLine()) != null){
rspList.add(line);
}
p.waitFor();//关闭通道
in.close();//关闭输入流
} catch (IOException e1) {
e1.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return rspList;
}
}
//获取当前系统环境并设置新窗口
public static String getOsCmd() {
Properties props = System.getProperties(); //获得系统属性集
String osName = props.getProperty("os.name"); //操作系统名称
if (osName.toLowerCase().contains("linux")) {
return "/bin/bash";
} else if (osName.toLowerCase().contains("windows")) {
return "cmd";
} else {
throw new BaseRuntimeException("服务器不是linux|windows操作系统");
}
}
总结
在使用的过程中先知道了Runtime,在使用中也遇到过问题有
1:在操作系统执行命令需要有个起始命令。都是调起命令窗口,这就是为什么结尾都会要加一条exit命令最好。
2:Windows的是 cmd 。如果加/c则适合一条整命令的写法。可以理解为主动帮你在命令后面加了个exit命令。Linux的是/bin/bash和/bin/sh。sh本质上还是bash。如果加-c和前面理解一样
3:返回的字符是GB2312编码格式,如果在windows上中文就很容易看出乱码,所以上面才转码。
4:Windows多行命令用&&拼接,Linux多行命令用;拼接,但实际Linux用&&也行。
5:多行命令拼接为一条如果起始命令之后是cd命令则windows上有效,Linux上无效(会不会只有我┭┮﹏┭┮)
最后发现Runtime与jdk1.5之后的ProcessBuilder在实现的本质上是一样的,但实现的方式可以看出ProcessBuilder的方式更加的简单方便,虽然最后我还是用的是Runtime实现(因为最先看到它),但也理解了ProcessBuilder的使用方式。