Java execute command

在Java的使用过程中,难免需要去执行linux命令(执行shell也是linux命令),那么应该如何做呢?本文将进行一些演示。

所依赖的相关类

要在Java中执行linux命令有两种方式,依赖于三个类。我们先介绍这三个类,然后在使用这三个类,组合两种方案来进行说明。

java.lang.Process

概括

ProcessBuilder.start()和Runtime.exec方法创建一个本地进程,并返回一个Process子类的实例,该实例用来控制进程并获得相关信息。Process类提供了执行
当Process对象没有更多的引用时,不是删除子进程,而是继续异步执行子进程。

相关API

方法名 描述
abstract void destory() 销毁子进程
abstract int exitValue() 获取子进程的出口值
abstract InputStream getErrorStream() 获取子进程的错误流
abstract InputStream getInputStream() 获取子进程的输入流
abstract OutputStream getOutputStream() 获取子进程的输出流
abstract int waitFor() 导致当前线程等待,如有必要,一直要等待由该Process对象表示的进程已经终止

java.lang.Runtime

概括

每个Java应用程序都有一个Runtime类实例,是应用程序能够与其运行环境相连接。可以通过getRuntime方法获取当前运行环境。Process类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁进程的方法。
创建的子进程没有自己的终端或控制台。它的所有标准IO操作都是通过三个流(getOutputStream()、getInputStream()和getErrorStream())重定向到父进程的。父进程使用这些流来提供到子进程的输入和获得从子进程的输出。因为有些本机平台对标准输入和输出流提供优先的缓冲区大小,如果读写子进程的输入流或输出流迅速出现失败,则可能导致子进程阻塞,甚至发生死锁。
当没有Process对象的更多引用时,不是删除子进程,而是继续异步的执行子进程。
对于带有Process对象的Java进程,没有必要异步或并发的执行由Process对象表示的进程。

相关API

方法名 描述
void addShutdownHook(Thread hook) 注册新的虚拟机关闭钩子
int availableProcessors() 返回Java虚拟机返回可用的处理器数目
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) 在指定的环境和目录的独立进程中执行字符串指定的命令
void exit(int status) 通过启动虚拟机的关闭序列,终止当前正在运行的JVM
long freeMemory() 获取JVM当前的空闲内存量
void gc() 运行垃圾回收器
Runtime getRuntime() 返回当前Java应用相关的运行时对象
void halt(int status) 强制终止目前正在运行的JVM
void load(String filename) 加载作为动态库的指定文件名
void loadLibrary(String libname) 加载具有指定库名的动态库
long maxMemory() 返回Java虚拟机试图使用的最大内存量
boolean removeShutdownHook(Thread hook) 取消注册某个先前已注册的虚拟机关闭钩子
void runFinalization() 运行挂起finalization的所有对象的终止方法
long totalMemory() 返回Java虚拟机的内存总量
void traceInstructions(boolean on) 启用/禁止指令跟踪
void traceMethodCalls(boolean on) 启用/禁用方法调用跟踪

这里最需要关注的是exec相关的重载方法和getRuntime()方法。

java.lang.ProcessBuilder

概括

此类用于创建操作系统进程。
每个ProcessBuilder实例管理一个进程属性集。start()方法利用这些属性创建一个新的Process实例。start()方法可以从同一实例反复调用,以利用相同的或相关的属性创建新的子进程。
每个进程生成器管理这些进程属性:

命令是一个字符串列表,他表示要调用的外部程序文件及参数(如果有)。再次,表示有效的操作系统命令的字符串列表是依赖于系统的。
环境是从变量到值的依赖于系统的映射。初始值是当前进程环境的一个副本(参阅System.getenv())。
工作目录。默认值是当前进程的当前工作目录,通常根据系统属性user.dir来命名。
redirectErrorStream属性。最初,该属性为false,意思是子进程的标准输出和错误输出被发送给两个独立的流,这些流可以通过Process.getEnputStream()和Process.getErrorStream()方法来访问。如果将值设置为true,标准错误将与标准输出合并。这使得关联错误消息和响应的输出变得更容易。再次情况下,合并的数据可以从Process.getInputStream()返回的流中读取,而从Process.getErrorStream()返回的流中读取将直接到达文件尾。

修改进程构建器的属性将影响后续由该对象的start()方法启动的进程,但不会影响以前启动的进程或Java自身的进程。
注意,此类不是同步的。如u哦多个线程同时访问一个ProcessBuilder,而其中至少一个线程从结构上修改了其中一个属性,它必须保持外部同步。

相关API

方法名 描述
List command() 返回此进程生成器的操作系统程序和参数
ProcessBuilder command(List command) 设置此进程生成器的操作系统程序和参数
ProcessBuilder command(String… command) 设置此进程生成器的操作系统程序和参数
File directory() 返回次进程生成器的工作目录
ProcessBuilder directory(File directory) 设置此进程生成器的工作目录
Map environment 返回此进程生成器环境的字符串映射试图
boolean redirectErrorStream() 返回进程生成器是否合并标准错误和标准输出
ProcessBuilder redirectErrorStream(boolean redirectErrorStream) 设置此进程生成器是否合并标准错误和标准输出
Process start() 使用此进程生成器的属性启动一个新的进程

相关示例

使用Runtime和Process执行命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Runtime r = Runtime.getRuntime();
try {
File f = new File("/Users/");
Process p = r.exec(new String[]{"ls", "-h"}, new String[]{}, f);
p.waitFor();
InputStream is = p.getInputStream();
OutputStream os = p.getOutputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String info = br.readLine();
while(info != null) {
System.out.println(info);
info = br.readLine();
}
p.destroy();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}

使用ProcessBuilder和Process执行命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
try {
ProcessBuilder pb = new ProcessBuilder("ls", "-h");
Process p = pb.start();
p.waitFor();
InputStream is = p.getInputStream();
OutputStream os = p.getOutputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String info = br.readLine();
while(info != null) {
System.out.println(info);
info = br.readLine();
}
p.destroy();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}