前言
Java尽管五脏俱全但总有软肋,譬如获取CPU等硬件信息,当然咱们能够通过JNI调用C/C++来获取,但对于对C/C++和Windows API不熟的码农是一系列简单的学习和踩坑过程。那能不能通过简略一些、学习成本低一些的形式呢?答案是必定的,在性能实现放在首位的状况下,借他山之石是最简洁无力的做法。
意识java.lang.Runtime#exec
办法
作用:用于调用内部程序,并重定向内部程序的规范输出、规范输入和规范谬误到缓冲池。性能就是和windows的“运行”一样。
重载办法阐明
Runtime#exec(String command);
Runtime#exec(String command, String[] envp);
Runtime#exec(String command, String[] envp, File workdir);
Runtime#exec(String[] cmdArray);
Runtime#exec(String[] cmdArray, String[] envp);
Runtime#exec(String[] cmdArray, String[] envp, File workdir);
String[] envp
作为调用命令前设置的会话级环境变量。
1.1. 变量作用域:命令运行完结后,通过该参数设置的环境变量将生效;
1.2. 设置形式:variableName=variableValue
,如Process proc = r.exec("cmd /c dir > %dest%", new String[]{"dest=c:\\dir.txt"});
File workdir
用于设置当前工作目录,譬如咱们须要执行位于D:\tools下的echo.exe程序,那么能够这样调用Process proc = r.exec("echo.exec", null, new File("D:\\tools"));
String command
即为须要调用的内部程序,以及命令行参数等。Windows下调用系统命令,像dir等命令是由cmd解析器解释执行的,因而若间接写”dir”则会被认为在当前工作目录下有一个”dir.exe”文件,那么当然会执行失败;在Linux下调用ls等是同样情理,因而请按如下形式调用cmd和shell命令:
3.1. 调用CMD命令的形式为Process proc = r.exec(String.format("cmd /c %s", "cmd命令,如dir、type等"))
,若要启动一个新的Console执行命令,只须要将dir
改写为start dir
即可;
3.2. 调用Shell命令的形式为Process proc = r.exec(String.format("/bin/sh -c %s", "shell命令,如ls、cat等"))
,若要启动一个新的Terminal执行命令,只须要将ls
改写为xterm -e ls
即可;
String[] cmdArray
性能和String command
一样,但命令行的每个局部将作被独立分隔进去作为数组中的元素。如cmd /c dir
必须宰割为new String[]{"cmd", "/c", "dir"}
,而不能宰割为new String[]{"cmd /c", "dir"}
。
输出重定向
废话少说,间接看代码!
try {
String cmd = "cmd /c start cmd.exe";
Process child = Runtime.getRuntime().exec(cmd);
OutputStream output = child.getOutputStream();
output.write("cd C:/ /r/n".getBytes());
output.flush();
output.write("dir /r/n".getBytes());
output.close();
}
catch (IOException e) {}
输入重定向
留神:不反对间接应用>或>>执行规范输入重定向。
String cmd = "/path/to/getipconfig.bat"; // 本人写的bat脚本文件,外面蕴含ipconfig /all命令。
Process p;
try {
p = Runtime.getRunTime().exec(cmd);
InputStream input = p.getInputStream();
InputStreamReader streamReader = new InputStreamReader(input);
BufferedReader bufReader = new BufferedReader(streamReader);
String line = null;
while ((line = bufReader.readLine()) != null) {
System.out.println(line);
}
}
catch (IOException e) {}
为什么不能应用>
和>>
实现输入重定向呢?
通过Process实例.getInputStream()
和Process实例.getErrorStream()
获取的输出流和谬误信息流是缓冲池是以后Java程序提供的,而不是间接获取内部程序的规范输入流和规范谬误流。
即通过Runtime#exec
调用内部程序时,内部程序的规范输入流和规范谬误流曾经被Java程序接管。那么在命令中希图通过>
和>>
实现输入重定向显然已无成果。
另外,缓冲池的容量是肯定的,因而若内部程序在运行过程中一直向缓冲池输入内容,当缓冲池填满,那么内部程序将暂停运行直到缓冲池有空位可接管内部程序的输入内容为止。(采纳xcopy命令复制大量文件时将会呈现该问题)
解决办法就是以后的Java程序一直读取缓冲池的内容,从而为腾出缓冲池的空间。如:
Runtime r = Runtime.getRuntime();
try{
Process proc = r.exec("cmd /c dir"); // 假如该操作为造成大量内容输入
// 采纳字符流读取缓冲池内容,腾出空间
BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream(), "gbk")));
String line = null;
while ((line = reader.readLine()) != null){
System.out.println(line);
}
// 或采纳字节流读取缓冲池内容,腾出空间
// ByteArrayOutputStream pool = new ByteArrayOutputStream();
// byte[] buffer = new byte[1024];
// int count = -1;
// while ((count = proc.getInputStream().read(buffer)) != -1){
// pool.write(buffer, 0, count);
// buffer = new byte[1024];
// }
// System.out.println(pool.toString("gbk"));
int exitVal = proc.waitFor();
System.out.println(exitVal == 0 ? "胜利" : "失败");
}
catch(Exception e){
e.printStackTrace();
}
留神:内部程序在执行完结后将会主动敞开,否则不论是字符流还是字节流均因为既读不到数据,又读不到流结束符而呈现阻塞Java过程运行的状况。
简化输入输出重定向的java.lang.ProcessBuilder
性能和java.lang.runtime#exec一样
,只是java.lang.ProcessBuilder
仅接管命令行以数组模式传递给java.lang.ProcessBuilder#command()
而已。
根本应用
ProcessBuilder pb = new ProcessBuilder();
// pb.command("cmd /c dir"); 这样会报错
// 或者写到new ProcessBuilder("cmd", "/c", "dir")
pb.command("cmd", "/c", "dir");
try {
Process process = pb.start();
// 前面都是操作Process实例的办法
}
catch (IOException e){}
重定向
public ProcessBuilder redirectInput(File file) // 输出重定向
public ProcessBuilder redirectOutput(File file) // 输入重定向
public ProcessBuilder redirectError(File file) // 异样重定向
示例
try {
ProcessBuilder pb = new ProcessBuilder("cmd");
// 在规范输出中,通过换行符分隔多行命令。
// commands.txt的内容为
// javac Demo.java
// java Demo
File commands = new File("/path/to/commands.txt");
File error = new File("/path/to/error");
File output = new File("/path/to/output");
pd.redirectInput(commands);
pd.redirectOutput(output);
pd.redirectError(error);
pd.start();
}
catch(Exception e) {
e.printStackTrace();
}
java.lang.Process
API阐明
// 以非阻塞形式获取子过程执行的返回值(习惯0示意失常完结)。若子过程尚未实现时调用该办法,则会报异样`java.lang.IllegalThreadStateException`
int exitValue()
// 以阻塞形式获取子过程执行的返回值。若过程尚未实现则会期待子过程实现后才复原以后线程。
// 问题:若子过程无奈失常敞开,则会导致Java线程始终挂起;
// 返回值为子过程的退出码
int waitFor()。
// 如果超时前子过程完结,那么返回`true` ,否则返回`false`
boolean waitFor(long timeout, TimeUnit unit)
// 强行终止子过程,但调用后子过程不会马上被终止,所以立刻调`boolean isAlive()`办法可能会返回`true`,因而需配合`waitFor`应用。
void destory()
// 默认实现为调用`void destory()`办法。从JDK1.8开始提供。
Process destoryForcibly()
// 如果子过程还没完结则返回`true` 。从JDK1.8开始提供。
boolean isAlive()
// 获取子过程的异样输入流,如果子过程以`ProcessBuilder`创立,且通过`ProcessBuilder.redirectError`设置重定向,那么该办法返回`null`
InputStream getErrorStream()
// 获取子过程的规范输入流,如果子过程以`ProcessBuilder`创立,且通过`ProcessBuilder.redirectOutput`设置重定向,那么该办法返回`null`
InputStream getInputStream()
// 获取子过程的规范输出流,如果子过程以`ProcessBuilder`创立,且通过`ProcessBuilder.redirectInput`设置重定向,那么该办法返回`null`
OutputStream getOutputStream()
总结
尊重原创,转载请注明来自:https://www.cnblogs.com/fsjoh… ^_^肥仔John
发表回复