java 调用外部程序(Runtime.getRuntime().exec)

简介: java 调用外部程序(Runtime.getRuntime().exec)

参考:

概述

Runtime.getRuntime().exec 用于调用外部可执行程序或系统命令,并重定向外部程序的标准输入、标准输出和标准错误到缓冲池。功能和windows“运行”类似。

格式:

Process process = Runtime.getRuntime().exec( ".//p.exe ");
process.waitfor();

第一行的“.//p.exe”是要执行的程序名,Runtime.getRuntime() 返回当前应用程序的Runtime对象,该对象的 exec() 方法指示Java虚拟机创建一个子进程执行指定的可执行程序,并返回与该子进程对应的Process对象实例。通过Process可以控制该子进程的执行或获取该子进程的信息。

第二条语句的目的等待子进程完成再往下执行。


方法API

Runtime.getRuntime().exec共有六个重载方法:

// 在单独的进程中执行指定的外部可执行程序的启动路径或字符串命令
public Process exec(String command)
// 在单独的进程中执行指定命令和变量
public Process exec(String[] cmdArray)
// 在指定环境的独立进程中执行指定命令和变量
public Process exec(String command, String[] envp)
// 在指定环境的独立进程中执行指定的命令和变量
public Process exec(String[] cmdArray, String[] envp)
// 在指定环境和工作目录的独立进程中执行指定的字符串命令
public Process exec(String command, String[] envp, File dir)
// 在指定环境和工作目录的独立进程中执行指定的命令和变量
public Process exec(String[] cmdarray, String[] envp, File dir)
// 参数说明:
    cmdarray // 包含所调用命令及其参数的数组。数组第一个元素是命令,其余是参数
    envp     // 字符串数组,其中每个元素的环境变量的设置格式为 name=value,如果子进程应该继承当前进程的环境,则该参数为null
    dir         // 子进程的工作目录;如果子进程应该继承当前进程的工作目录,则该参数为null
    
// 参数cmdArray 示例:shutdown -s -t 3600
String arr[] = {"shutdown","-s","-t","3600"};
Process process = Runtime.getRuntime().exec(arr[]);
/*
注意:
    在调用这个方法时,不能将命令和参数放在一起,eg:String arr[] = {"shutdown -s -t 3600"};
    这样会导致程序把“shutdown -s -t 3600”当成是一条命令的名称,然后去查找“shutdown -s -t 3600”这条命令,它当然会找不到,所以就会报错
*/

注意:

  • Runtime.exec() 不是cmd或shell环境,因此无法直接调用dir等命令,需要在程序中读取运行的操作系统平台,以调用不同的命令解释器(NT:cmd.exe,windows 95/98:command.exe,linux:/bin/sh)


Procss类将持有该程序返回 Java VM 的引用。这个procss类是一个抽象类,具体子类的实现依赖于不同的底层操作系统。

Process 的常用方法:

// 导致当前线程等待,如有必要,一直要等到由该 Process 对象表示的进程已经终止。
int waitFor()
/*    如果已终止该子进程,此方法立即返回。
    如果没有终止该子进程,调用的线程将被阻塞,直到退出子进程,0 表示正常终止 */

// 杀掉子进程
void destroy()

// 返回子进程的出口值,值 0 表示正常终止
int exitValue()

// 获取子进程的错误流
InputStream getErrorStream()
// 获取子进程的输入流
InputStream getInputStream()
// 获取子进程的输出流
OutputStream getOutputStream()


程序阻塞问题

通过 Process实例.getInputStream() 和 Process实例.getErrorStream() 获取的输入流和错误信息流是缓冲池向当前Java程序提供的,而不是直接获取外部程序的标准输出流和标准错误流。

071448105966400.png

而缓冲池的容量是一定的,因此若外部程序在运行过程中不断向缓冲池输出内容,当缓冲池填满,那么外部程序将暂停运行直到缓冲池有空位可接收外部程序的输出内容为止。(采用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进程运行的情况。cmd的参数 “/c” 表示当命令执行完成后关闭自身。


实例

在当前目录执行dir命令,并将结果保存到 c:\dir.txt 文本文件中:

前提:假设当前用户的家目录为c:\user\fsjohnhuang

c:\user\fsjohnhuang下的目录结构

c:\user\fsjohnhuang 
|--jottings 
|--test.txt

d:\test下的目录结构

d:\test
|--movies
|--readme.txt

代码片段

Runtime r = Runtime.getRuntime();
try{
  Process proc = r.exec("cmd /c dir > %dest%", new String[]{"dest=c:\\dir.txt", new File("d:\\test")});
  int exitVal = proc.waitFor(); // 阻塞当前线程,并等待外部程序中止后获取结果码
  System.out.println(exitVal == 0 ? "成功" : "失败");
}
catch(Exception e){
  e.printStackTrace();
}

执行代码后查看c:\dir.txt文件内容如如下:

驱动器 D 中的卷没有标签。
卷的序列号是 8074-B214
 
 D:\test 的目录
 
2014/09/22  14:45    <DIR>          movies
2014/03/31  17:14             8,642 readme.txt


拓展

1,调用一次exec方法执行多条cmd命令,使用 && 分隔命令,该方法的局限性是只能在cmd里面使用

Runtime.getRuntime().exec("cmd /c set CLASSPATH=D:\\ && javac D:\\a.java && java a");

// 方法重载:public Process exec(String[] cmdarray, String[] envp, File dir)
String arr[] = {"CLASSPATH=D://","Path=C:\\Program Files\\Java\\jdk1.8.0_131\\bin"};
Runtime.getRuntime().exec("cmd /c javac a.java && java a", arr, new File("D://"));

2,执行Runtime.exec()需要注意的陷阱:https://www.cnblogs.com/fpqi/p/9679039.html

相关文章
|
1月前
|
Java 流计算
利用java8 的 CompletableFuture 优化 Flink 程序
本文探讨了Flink使用avatorscript脚本语言时遇到的性能瓶颈,并通过CompletableFuture优化代码,显著提升了Flink的QPS。文中详细介绍了avatorscript的使用方法,包括自定义函数、从Map中取值、使用Java工具类及AviatorScript函数等,帮助读者更好地理解和应用avatorscript。
利用java8 的 CompletableFuture 优化 Flink 程序
|
2月前
|
XML 存储 JSON
Java程序部署
Java程序部署
|
27天前
|
Java Maven 数据安全/隐私保护
如何实现Java打包程序的加密代码混淆,避免被反编译?
【10月更文挑战第15天】如何实现Java打包程序的加密代码混淆,避免被反编译?
41 2
|
30天前
|
安全 Java Linux
java程序设置开机自启
java程序设置开机自启
|
1月前
|
运维 Java Linux
【运维基础知识】Linux服务器下手写启停Java程序脚本start.sh stop.sh及详细说明
### 启动Java程序脚本 `start.sh` 此脚本用于启动一个Java程序,设置JVM字符集为GBK,最大堆内存为3000M,并将程序的日志输出到`output.log`文件中,同时在后台运行。 ### 停止Java程序脚本 `stop.sh` 此脚本用于停止指定名称的服务(如`QuoteServer`),通过查找并终止该服务的Java进程,输出操作结果以确认是否成功。
35 1
|
2月前
|
消息中间件 分布式计算 Java
Linux环境下 java程序提交spark任务到Yarn报错
Linux环境下 java程序提交spark任务到Yarn报错
41 5
|
2月前
|
Java 编译器 数据库连接
探索Java中的异常处理:提升程序的鲁棒性
【9月更文挑战第25天】在Java的世界里,异常是那些不请自来、令人头疼的“客人”。它们悄无声息地潜入我们的代码,一旦出现,便可能导致程序崩溃或行为异常。但是,如果能够妥善管理这些异常,我们就能将潜在的灾难转变为增强程序鲁棒性和用户体验的机会。本文将通过深入浅出的方式,带领读者理解Java异常处理的重要性,并提供实用的策略来优雅地处理这些意外情况。让我们一起学习如何在Java中捕捉、处理和预防异常,确保我们的程序即使在面对不可预见的错误时也能保持稳健运行。
|
2月前
|
Oracle Java 关系型数据库
Linux下JDK环境的配置及 bash: /usr/local/java/bin/java: cannot execute binary file: exec format error问题的解决
如果遇到"exec format error"问题,文章建议先检查Linux操作系统是32位还是64位,并确保安装了与系统匹配的JDK版本。如果系统是64位的,但出现了错误,可能是因为下载了错误的JDK版本。文章提供了一个链接,指向Oracle官网上的JDK 17 Linux版本下载页面,并附有截图说明。
Linux下JDK环境的配置及 bash: /usr/local/java/bin/java: cannot execute binary file: exec format error问题的解决
|
1月前
|
Java Python
如何通过Java程序调用python脚本
如何通过Java程序调用python脚本
27 0
|
2月前
|
监控 Java 数据库
Java程序如何进行不停机更新?
Java程序如何进行不停机更新?
94 1