Java魔法堂之调用外部程序的方法
作者:^_^肥仔John 发布时间:2023-11-09 07:14:16
前言
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/fsjohnhuang/p/14396218.html


猜你喜欢
- 概述HashTable是jdk 1.0中引入的产物,基本上现在很少使用了,但是会在面试中经常被问到,你都知道吗:HashTable底层的实现
- 引言今天给大家介绍一个非常有意思类库,基于java实现的简繁体转换,适用于后端、android等开发领域源码地址:* liuyueyi/qu
- Spring Boot 自动装配最重要的注解@SpringBootApplication@Target(ElementType.TYPE)@
- 目录一 . 前言二 . 流程梳理2.1 实例化创建2.1.1 doGetBean 入口2.1.2 doGetBean 补充节点2.1.3 A
- 导入项目集成环境:IntelliJ IDEA 2020.1.2演示系统:DELL Windows 10Eclipse项目如何导入IDEA并成
- 1.返回ModelAndView对象(.jsp)controller代码:package controller;import java.ut
- 说明Flutter原生是没有支持数据库操作的,它使用SQLlit插件来使应用具有使用数据库的能力。其实就是Flutter通过插件来与原生系统
- 在开发过程中,不少有Spring Aop的使用,在面向切面编程时,我们会使用< aop:aspect>;在进行事务管理时,我们会
- 返回集合为null还是空集合及空集合的三种写法个人认为在自己写接口时,需要返回集合时返回一个空集合,比如mybatis查询如果返回一个集合,
- selenium 中如何处理弹出窗口阅读目录原理测试页面的HTMLJava 代码原理在代码里, 通过 &n
- 在Eclipse中创建Android项目,利用之前学过的WebView控件和中国天气网提供的天气数据接口,实现获取指定城市的天气预报。布局文
- paras.xml文件<?xml version="1.0" encoding="UTF-8"
- 1. 父工程构建1.1 Maven项目搭建环境版本JDK1.8Maven3.6+Maven模板maven-archetype-size删除父
- 前段时间做项目,需要把别人做的hbuilder项目,添加到自己的app中,找了官网、博客,最终终于费了老大的劲实现了,今天就给大家分享一下我
- C# httpwebrequest访问HTTPS链接时遇到这个错误,但是如果我开抓包工具,比如filddler2,则POST返回正常错误提示
- byte:java中最小的数据类型。1字节/8位。-128(2^7)~127(2^7-1),默认值0。short:短整型,2字节/16位,取
- 这篇文章主要介绍了Java如何把int类型转换成byte,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的
- 项目地址: GITHUB (本地下载)java mybatis 多表查询简介实现简单的实体类操作多表, 首先你的项目是
- 更正说明:我之前的的标题有点文不对题,我这篇博客的内容明明说的是:java中对象创建的过程,对内存之种底层的东西,我其实提的不太多。所以我原
- 一、场景描述建造者模式同工厂模式、抽象工厂模式一样,用于创建继承类对象。工厂模式:Java设计模式之工厂模式抽象工厂模式:Java设计模式之