通过Java执行系统命令,与cmd中或者终端上一样执行shell命令一样。
最典型的用法:
1、Runtime.getRuntime().exec();会在给定的环境和工作目录下启动一个独立的进程运行外部命令。
2、new ProcessBuilder(cmdArray).start();
从JDK1.5开始,官方提供并推荐使用ProcessBuilder类进行shell命令操作。
首先介绍Runtime类提供了如下方法:
public long totalMemory();//返回所有可用内存空间
public long maxMemory();//返回最大可用内存空间
public long freeMemory();//返回空余内存空间
public void gc();(如果内存东西过大)手动实现JVM的gc机制。
public Process exec(String command)throws IOException//执行外部命令
Runtime.getRuntime().availableProcessors();//返回jvm可用的处理器数量
Runtime常见的几个重写的参数方法:
public Process exec(String command) throws IOException
public Process exec(String cmdarray[]) throws IOException
public Process exec(String command, String[] envp) throws IOException
public Process exec(String command, String[] envp, File dir) throws IOException
public Process exec(String[] cmdarray, String[] envp) throws IOException
public Process exec(String[] cmdarray, String[] envp, File dir) throws IOException
对上面进行解释:
1、command:一个命令,可能包含参数和命令
2、envp:环境变量的设置;格式(name=value,name=null),null表示子进程继承当前的运行时的环境。
3、dir:子进程工作目录,null表示继承当前进程的工作目录
4、cmdarray:包含多个命令的数组,可以是命令也可以是参数
简单实现:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import org.junit.Test;
public class Demo {
@Test
public void testName() throws Exception {
// 调用命令
Runtime runtime = Runtime.getRuntime();
Process process = null;
try{
process = runtime.exec("javac"); // 用法1:调用一个外部程序
// process = runtime.exec("cmd /c dir"); // 用法2:调用一个指令(可包含参数)
// process = runtime.exec("cmd /c test.bat", null, new File("D:\\test.test.bat")); // 用法3:调用一个.bat文件或者exe文件
// 存放要输出的行数据
String line = null;
// 输出返回的报错信息
System.out.println("=================ErrorStream===================");
BufferedReader inErr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
while ((line = inErr.readLine()) != null) {
System.out.println(line);
}
// 输出正常的返回信息
System.out.println("=================InputStream===================");
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((line = in.readLine()) != null) {
System.out.println(line);
}
// 获取退出码
int exitVal = process.waitFor(); // 等待进程运行结束
//如果返回0表示执行成功,返回1表示在还行失败
System.out.println("exitVal = " + exitVal);
} catch (Exception e) {
e.printStackTrace();
}finally{
if (process != null) {
process.destroy();
}
}
}
}
使用Process.waitFor()
和Process.exitValue()的区别:
Process.waitFor()
与Process.exitValue()
同样会返回外部程序的执行结果,但是exitValue()
会阻塞直到外部程序运行结束。
指令介绍:
cmd /c dir 是执行完dir命令后关闭命令窗口。
cmd /k dir 是执行完dir命令后不关闭命令窗口。
cmd /c start dir 会打开一个新窗口后执行dir指令,原窗口会关闭。
cmd /k start dir 会打开一个新窗口后执行dir指令,原窗口不会关闭。
Runtime.exec()
不是一个命令行工具:相对于系统命令行工具,Runtime.exec(..)
会更加局限并且不具备跨平台功能,命令行能接收的任意字符串,Runtime.exec(..)
并不一定能接收。这个陷阱通常出现在Runtime.exec(String)
这个接收单个字符串执行外部命令的方法中。造成这个混乱的原因之一在于Runtime.exec(String)
将传递的字符串作为外部命令的参数,而命令行中可以区分一个字符串中的多个命令。
如何解决?
就是把jechoHTT类里面输出的内容,保存到test.txt
public class CommandLineExec {
public static void main(String[] args) throws IOException, InterruptedException {
FileOutputStream fos = new FileOutputStream("test.txt");
Process pid = Runtime.getRuntime().exec("java Test$jechoHTT");
new Thread(new OutputHandlerRunnable(pid.getInputStream(), fos)).start();
new Thread(new OutputHandlerRunnable(pid.getErrorStream())).start();
int exitValue = pid.waitFor();
System.out.println("Process exitValue: " + exitValue);
fos.flush();
fos.close();
}
private static class OutputHandlerRunnable implements Runnable {
private InputStream in;
private OutputStream os;
public OutputHandlerRunnable(InputStream in) {
this.in = in;
}
// add redirect
public OutputHandlerRunnable(InputStream in, OutputStream redirect) {
this(in);
this.os = redirect;
}
@Override
public void run() {
try (BufferedReader bufr = new BufferedReader(new InputStreamReader(this.in))) {
PrintWriter pw = null;
if (this.os != null) {
pw = new PrintWriter(this.os);
}
String line = null;
while ((line = bufr.readLine()) != null) {
System.out.println(line);
if (pw != null) {
// redirect
pw.println(line);
}
}
if (pw != null) {
pw.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static class jechoHTT {
public static void main(String[] args) {
System.out.println("Hello World!!!");
}
}
}
我们在介绍一下ProcessBuilder这个方法执行外部命令:
优点:配置环境变量时更优雅、对当前目录的控制也更合理、错误流重定向特别方便 、进程控制更简洁。
ProcessBuilder基本方法介绍:
构造方法摘要
ProcessBuilder(List<String> command)
利用指定的操作系统程序和参数构造一个进程生成器。
ProcessBuilder(String... command)
利用指定的操作系统程序和参数构造一个进程生成器。
方法摘要
command()
返回此进程生成器的操作系统程序和参数。
command(List<String> command)
设置此进程生成器的操作系统程序和参数。
command(String... command)
设置此进程生成器的操作系统程序和参数。
directory()
返回此进程生成器的工作目录。
directory(File directory)
设置此进程生成器的工作目录。
environment()
返回此进程生成器环境的字符串映射视图。
redirectErrorStream()
通知进程生成器是否合并标准错误和标准输出。
redirectErrorStream(boolean redirectErrorStream)
设置此进程生成器的 redirectErrorStream 属性。
start()
使用此进程生成器的属性启动一个新进程。
基本用法:
public class ProcessTool {
public static void main(String[] args) throws IOException {
execWindowCmd();
}
public static void execWindowCmd() throws IOException {
ProcessBuilder pb = new ProcessBuilder();// 命令
Map<String, String> env = pb.environment();// 独立环境变量
System.out.println(env);// 打印环境变量
env.put("MY_NAME", "KING");// 添加环境变量key-value
pb.redirectErrorStream(true);// 重定向错误输出流到正常输出流
try {
pb.directory(new File("d://test1"));// 设置目录test1
pb.command("cmd", "/c", "dir");// 执行命令
Process process1;
process1 = pb.start();// 启动进程
BufferedReader br1;
br1 = new BufferedReader(new InputStreamReader(process1.getInputStream(), "gbk"));
String line1 = null;
while ((line1 = br1.readLine()) != null) {
System.out.println(line1);
}
pb.directory(new File("d://test2"));// 设置目录test2
pb.command("cmd", "/c", "dir", ">>", "test1.log");// 执行命令,把结果输出到test1.log
Process process2 = pb.start();// 启动进程
BufferedReader br2 = new BufferedReader(new InputStreamReader(process2.getInputStream(), "gbk"));
String line2 = null;
while ((line2 = br2.readLine()) != null) {//因为结果输出到了文件,所以本处无信息返回
System.out.println(line2);
}
} catch (IOException e) {
e.printStackTrace();
throw e;
}
}
}
ProcessBuilder和Process的区别:
1、ProcessBuilder为进程提供了更多的控制,而Process的功能相对来说简单的多
2、ProcessBuilder是一个final类,有两个带参数的构造方法,你可以通过构造方法来直接创建ProcessBuilder的对象。而Process是一个抽象类,一般都通过Runtime.exec()和ProcessBuilder.start()来间接创建其实例。
ProcessBuilder的api:
内部类
方法 | 描述 |
| 表示子进程的输入源或者输出的目的 |
构造器
方法 | 描述 |
| 使用操作系统程序 和参数创建一个进程生成器 |
| 使用操作系统程序和参数创建一个进程生成器 |
方法
取出或设置程序和参数的方法
方法 | 描述 |
| 返回操作系统程序和该程序的参数。 |
| 设置这个进程生成器的 操作系统程序 和 参数。 |
| 置这个进程生成器的 操作系统程序 和 参数。 |
取出或设置工作目录的方法
方法 | 描述 |
| 返回此进程生成器的工作目录。 |
| 设置进程生成器的工作目录。 |
设置标准 IO 的方法
方法 | 描述 |
| 使用文件作为标准输出 |
| 使用 |
| 重定向标准输入到文件中 |
| 重定向标准输入 |
| 将标出错误输出设置到文件中 |
| 设置标准错误输出的目标 |
取出标准 IO 的方法
方法 | 描述 |
| 返回表示 |
| 返回表示 |
| 返回表示 |
合并标准输出相关的方法
方法 | 描述 |
| 子进程和父进程使用相同的标准输入,标准输出,标准错误输出 |
| 设置 |
| 判断进程生成器是否合并了标准错误输出和标准输出。 |
其他方法
方法 | 描述 |
| 返回保存进程生成器的环境变量的 |
启动进程方法
方法 | 描述 |
| 使用进程生成器中设置的属性启动新一个新的进程 |
Process 类 API 整理
杀死子进程
方法 | 描述 |
| 杀死子流程 |
| 杀死子流程 |
获取子进程的 IO
方法 | 描述 |
| 返回连接到子进程的错误输出的输入流 |
| 返回连接到子进程的正常输出的输入流. |
| 返回连接到子进程的正常输入的输出流 |
其他方法
方法 | 描述 |
| 返回子进程的退出值 |
| 测试子流程是否有效 |
等待子进程
方法 | 描述 |
| 使当前线程等待 |
| 使当前线程在必要时等待,直到此 |
以上内容均来自网上参考整理。