关键字 Java 外部程序 CMD 进程 调用 Process
最近接触一个需求,是利用Java调用本地命令行程序,并希望Java程序能与该命令行程序进行交互,Java对该程序的操作如同在终端中对程序的操纵一样。
在技术调研的过程中,遇到了几个问题:
如何Java调用命令行程序
如何利用Java向命令行程序的标准输入写入字符流
如何利用Java即时地得到命令行程序的标准输出流。
如何利用Java即时地得到命令行程序的标准错误流
一、调用命令行程序
这个很简单,Java调用的方法为
Process process = Runtime.getRuntime().exec(“command name”);
二、标准输出
注意,在这里标准输出指的是Java程序以标准输出的方式发出字节流,这些字节流会以标准输入的方式进入被调用的命令行程序
OutputStream pOutputStream = process.getOutputStream();
PrintWriter outputWriter = new PrintWriter(pOutputStream, true);
outputWriter.print(string);
PrintWriter的第二个构造参数一定要选为true,这样才能自动flush进入外部程序,不然,没有Flush,你向被调用程序所写的输入,只有在下一次缓冲被Flush的时候才能发挥作用,这样,当你的输入很少时,你虽然在代码里print了命令,但是外部程序并没有得到他,就一直阻塞,这是开发者经常会遇到的问题。
三、标准输入和错误输入
private InputStream pErrorStream = process.getErrorStream();
private InputStream pInputStream =process.getInputStream();
这两个输入是用来接受外部程序的反馈的,外部程序通常会向标准终端打印字符,这些字符会通过这两个流得到,经过测试,我们发现一个问题,如果外部程序在输出信息时,没有用flush也会出现问题,比如C语言的程序
scanf(“%d”, &i);
printf(“%d”, i);
这段代码在运行时,虽然在终端里会即时的显示出来,但是却不能及时地发送给pInputStream,这是因为程序输出使用了缓冲机制造成的,所以,这造成的困难是如果你没有外部程序的源码,你就很难将输出即时显示出来,我目前还没有找到解决方案,如果你有源码就好办了,在源码中设置输出为即时flush就好了,我用笨办法来说明:
scanf(“%d”, &i);
printf(“%d”, i);
fflush(stdout);
这样,fflush(stdout)之后,pInputStream就会得到输入了。
四、综合
下面我们用三个线程来进行一个简单的与外部程序的交互过程的设计
线程一、
process.waitFor(),负责建立线程并等待线程结束
线程二、
for (int i = 0; i > -1; i = pInputStream.read(inBuffer)) {
// We have a new segment of input, so process it as a String..
System.out.print(new String(inBuffer, 0, i));
}
负责接收外部程序的输出信息
线程三、
// Read the ErrorStream in a loop until we find no more bytes to read..
for (int i = 0; i > -1; i = pErrorStream.read(errBuffer)) {
// We have a new segment of error, so process it as a String..
Systerm.err.print(new String(errBuffer, 0, i));
}
负责接收外部程序的错误输出信息
在适当的地方,调用outputWriter.print(string);向程序写入字符流。
===========================================================================================================
1 java调用外部程序的方法
在一个java应用中,可能会遇到这样的需求,就是需要调用一些外部的应用做一些处理,比如调用excel,然后在继续程序的运行。
下面就开始进入java调用外部程序的一些演示,让java应用更加灵活。
1:最简单的演示:
Runtime.getRuntime().exec("notepad.exe");
记事本被打开了是吧。
2:传递应用程序的参数:
Runtime runtime=Runtime.getRuntime();
String[] commandArgs={"notepad.exe","c:/boot.ini"};
runtime.exec(commandArgs);
现在不单单打开了记事本,而且还装载了boot.ini文件是吧。
现在已经完全解决了调用外部程序的问题,不是吗,但是大家会发现exec方法是有返回值,那么继续我们的演示吧。
1:Process的waitFor:
Runtime runtime=Runtime.getRuntime();
String[] commandArgs={"notepad.exe","c:/boot.ini"};
Process process=runtime.exec(commandArgs);
int exitcode=process.waitFor();
System.out.println("finish:"+exitcode);
执行上面的代码以后发现不同的地方了吗,waitFor会使线程阻塞,只有外部程序退出后才会执行System.out.println("finish:"+exitcode);
这个功能很有用是吧,因为多数时候你都需要等待用户处理完外部程序以后才继续你的java应用。
2:Process的destroy:
Runtime runtime=Runtime.getRuntime();
String[] commandArgs={"notepad.exe","c:/boot.ini"};
final Process process=runtime.exec(commandArgs);
new Thread(new Runnable(){
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {}
process.destroy();
}}).start();
int exitcode=process.waitFor();
System.out.println("finish:"+exitcode);
这个演示稍微复杂了一些,如果你等待5秒,就会发现记事本自动关闭了,是的,这个就是destroy方法的作用,强制关闭调用的外部程序。
不用我解释了吧,这是非常有用的方法。
以上的部分已经足够你调用并控制你的外部应用了。如果需要更详细的信息,看javadoc文档吧。
最后的说明:ProcessBuilder这个1.5新增的类也可以完成同样的任务,Runtime就是调用了这个类。
============================================================================================================
Java调用外部程序命令主要用到两个类:
java.lang.Runtime
每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。可以通过 getRuntime 方法获取当前运行时。应用程序不能创建自己的 Runtime 类实例。
java.lang.Process
ProcessBuilder.start() 和 Runtime.exec 方法创建一个本机进程,并返回 Process 子类的一个实例,该实例可用来控制进程并获取相关信息。Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的方法。 对于带有 Process 对象的 Java 进程,没有必要异步或并发执行由 Process 对象表示的进程。
下面Java调用Windows命令的例子:
下面Java调用Windows命令的例子:
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2008-7-18
* Time: 14:18:27
* Java调用Windows命令测试
*/
public class TestCmd {
public static void main(String args[]) {
testWinCmd();
dirOpt();
}
public static void testWinCmd() {
System.out.println("------------------testWinCmd()--------------------");
Runtime runtime = Runtime.getRuntime();
System.out.println(runtime.totalMemory());
System.out.println(runtime.freeMemory());
System.out.println(runtime.maxMemory());
System.out.println(runtime.availableProcessors()); //处理器数
try {
//执行一个exe文件
runtime.exec("notepad");
runtime.exec("C://Program Files//Microsoft Office//OFFICE11//winword.exe c://test.doc");
//执行批处理
runtime.exec("c://x.bat");
//执行系统命令
runtime.exec("cmd /c dir ");
runtime.exec("cmd /c dir c://");
// //-------------- 文件操作 --------------
runtime.exec("cmd /c copy c://x.bat d://x.txt"); //copy并改名
runtime.exec("cmd /c rename d://x.txt x.txt.bak"); //重命名
runtime.exec("cmd /c move d://x.txt.bak c://"); //移动
runtime.exec("cmd /c del c://x.txt.bak"); //删除
//-------------- 目录操作 --------------
runtime.exec("cmd /c md c://_test"); //删除
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 执行批处理文件,并获取输出流重新输出到控制台
*/
public static void dirOpt() {
System.out.println("------------------dirOpt()--------------------");
Process process;
try {
//执行命令
process = Runtime.getRuntime().exec("c://x.bat");
//取得命令结果的输出流
InputStream fis = process.getInputStream();
//用一个读输出流类去读
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
String line = null;
//逐行读取输出到控制台
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* Created by IntelliJ IDEA.
* User: leizhimin
* Date: 2008-7-18
* Time: 14:18:27
* Java调用Windows命令测试
*/
public class TestCmd {
public static void main(String args[]) {
testWinCmd();
dirOpt();
}
public static void testWinCmd() {
System.out.println("------------------testWinCmd()--------------------");
Runtime runtime = Runtime.getRuntime();
System.out.println(runtime.totalMemory());
System.out.println(runtime.freeMemory());
System.out.println(runtime.maxMemory());
System.out.println(runtime.availableProcessors()); //处理器数
try {
//执行一个exe文件
runtime.exec("notepad");
runtime.exec("C://Program Files//Microsoft Office//OFFICE11//winword.exe c://test.doc");
//执行批处理
runtime.exec("c://x.bat");
//执行系统命令
runtime.exec("cmd /c dir ");
runtime.exec("cmd /c dir c://");
// //-------------- 文件操作 --------------
runtime.exec("cmd /c copy c://x.bat d://x.txt"); //copy并改名
runtime.exec("cmd /c rename d://x.txt x.txt.bak"); //重命名
runtime.exec("cmd /c move d://x.txt.bak c://"); //移动
runtime.exec("cmd /c del c://x.txt.bak"); //删除
//-------------- 目录操作 --------------
runtime.exec("cmd /c md c://_test"); //删除
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 执行批处理文件,并获取输出流重新输出到控制台
*/
public static void dirOpt() {
System.out.println("------------------dirOpt()--------------------");
Process process;
try {
//执行命令
process = Runtime.getRuntime().exec("c://x.bat");
//取得命令结果的输出流
InputStream fis = process.getInputStream();
//用一个读输出流类去读
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
String line = null;
//逐行读取输出到控制台
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
下面Java调用Perl命令的例子:
//PerlExecResult is a user-defined class, It just saves execPerl's results
public static PerlExecResult execPerl(){
String[] cmd = { "perl", "pprogram.pl", "param1", "param2" };
StringBuffer resultStringBuffer = new StringBuffer();
String lineToRead = "";
//get Process to execute perl, get the output and exitValue
int exitValue = 0;
try{
Process proc = Runtime.getRuntime().exec( cmd );
InputStream inputStream = proc.getInputStream();
BufferedReader bufferedRreader =
new BufferedReader( new InputStreamReader( inputStream ) );
//save first line
if( ( lineToRead = bufferedRreader.readLine() ) != null ){
resultStringBuffer.append( lineToRead );
}
//save next lines
while( ( lineToRead = bufferedRreader.readLine() ) != null ){
resultStringBuffer.append( "/r/n" );
resultStringBuffer.append( lineToRead );
}
//Always reading STDOUT first, then STDERR, exitValue last
proc.waitFor(); //wait for reading STDOUT and STDERR over
exitValue = proc.exitValue();
}catch( Exception ex ){
resultStringBuffer = new StringBuffer( "" );
exitValue = 2;
}
PerlExecResult perlExecResult = new PerlExecResult( resultStringBuffer.toString(), exitValue );
return perlExecResult;
}
//PerlExecResult is a user-defined class, It just saves execPerl's results
public static PerlExecResult execPerl(){
String[] cmd = { "perl", "pprogram.pl", "param1", "param2" };
StringBuffer resultStringBuffer = new StringBuffer();
String lineToRead = "";
//get Process to execute perl, get the output and exitValue
int exitValue = 0;
try{
Process proc = Runtime.getRuntime().exec( cmd );
InputStream inputStream = proc.getInputStream();
BufferedReader bufferedRreader =
new BufferedReader( new InputStreamReader( inputStream ) );
//save first line
if( ( lineToRead = bufferedRreader.readLine() ) != null ){
resultStringBuffer.append( lineToRead );
}
//save next lines
while( ( lineToRead = bufferedRreader.readLine() ) != null ){
resultStringBuffer.append( "/r/n" );
resultStringBuffer.append( lineToRead );
}
//Always reading STDOUT first, then STDERR, exitValue last
proc.waitFor(); //wait for reading STDOUT and STDERR over
exitValue = proc.exitValue();
}catch( Exception ex ){
resultStringBuffer = new StringBuffer( "" );
exitValue = 2;
}
PerlExecResult perlExecResult = new PerlExecResult( resultStringBuffer.toString(), exitValue );
return perlExecResult;
}
=================================================================================================
几乎所有的Java 集成开发环境都需要调用外部进程进行Java程序的构建,编译,运行和调试,Eclipse,NetBeans,JBuilder和Intellij IDLE概莫例外。在执行过程中,将提示信息以黑色全部打印在控制台里,将异常和错误以红色方式打印。以非常醒目交互体验让程序员远离枯燥和乏味。
现在让我们以Eclipse为例来看看它如何工作的,以揭开它神秘面纱,探究隐藏在后面的秘密。
上篇主要介绍了JAVA IDE Console通过采用Runtime.getRuntime.exec()执行外部程序后,将返回一个Process对象. Process对象能返回三个流:
getInputStream(),对应Process程序的标准输出流。
getErrorStream(), 对应Process程序的标准错误输出流。
getOutputStream();对应Process程序的标准输入流。
函数名之所以与Process程序的方向相反,原因是站在Java Host程序的角度讲的。
现在我们应用此原理来仿真IDE 执行外部程序的过程。
列表1:ConsoleSimulator.java
package helloworld;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
/**
* Class for console simulation
*
* @author lewhwa
*/
public class ConsoleSimulator implements Runnable {
private volatile boolean isStop = false ;
private static final int INFO = 0 ;
private static final int ERROR = 1 ;
private InputStream is;
private int type;
/** Creates a new instance of StreamInterceptor */
public ConsoleSimulator(InputStream is, int type) {
this .is = is;
this .type = type;
}
public void run() {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader reader = new BufferedReader(isr);
String s;
try {
while (( ! isStop) && (s = reader.readLine()) != null ) {
if (s.length() != 0 ) {
if (type == INFO) {
System.out.println( " INFO> " + s);
} else {
System.err.println( " ERROR> " + s);
}
try {
Thread.sleep( 10 );
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
public void stop() {
isStop = true ;
}
public static void main(String[] args) throws IOException,
InterruptedException {
// Process child = Runtime.getRuntime().exec("run.bat");
Process child = Runtime.getRuntime().exec( " java -classpath bin helloworld.Test " );
OutputStream os = child.getOutputStream();
InputStream stdin = child.getInputStream(); //
InputStream stderr = child.getErrorStream();
Thread tIn = new Thread( new ConsoleSimulator(stdin, INFO));
Thread tErr = new Thread( new ConsoleSimulator(stderr, ERROR));
tIn.start();
tErr.start();
int result = child.waitFor();
tIn.join();
tErr.join();
if (result == 0 ) {
System.out.println( " SUCCESS! " );
} else {
System.out.println( " FAILED! " );
}
}
}
外部Bat文件:
列表2
time / t
cmd.exe / C / Q copy
javac
cmd.exe / C tree
rem c:\Designer_v5. 1 .0_win32_x86.exe c:\Designer_v5. 1 .0_win32_x861.exe
time / t
测试Java类Test.java
列表3:
package helloworld;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
/**Test Class
* @author lewhwa
*
*/
public class Test {
public static void main(String[] args) throws IOException {
FileReader fir = new FileReader("src/helloworld/Test1.java");
BufferedReader br = new BufferedReader(fir);
String s;
while((s=br.readLine())!=null){
System.out.println(s);
}
fir.close();
}
}
当ConsoleSimulator程序执行外部的run.bat时,输出如图1所示:
图1
当ConsoleSimulator程序执行外部的java test正常时,输出如图2所示:
图2
当ConsoleSimulator程序执行外部的java test发生异常时,输出如图3所示:
图3
综上,虽然没有在自己的GUI里将stdout和stderr进行说明,只是用ERROR>提示符和INFO>提示符进行演示,但是完全IDE Console的原理。对ConsoleSimulator稍加修改,完全放入到自己的应用程序当中去。
在我们进行Java程序开发的过程当中,可能涉及到其它的应用程序,借助这种技术,可以很好利用它们,将它们集成到自己的应用当中,将极大地缩短开发周期,何乐而不为呢!